ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

393 lines
8.9 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
  4. //
  5. // See accompanying file COPYING.TXT file for licensing details.
  6. //
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #define CPPCMS_SOURCE
  9. #if defined(__sun)
  10. #define _POSIX_PTHREAD_SEMANTICS
  11. #endif
  12. #include "session_posix_file_storage.h"
  13. #include <cppcms/cppcms_error.h>
  14. #include <cppcms/config.h>
  15. #include "crc32.h"
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include <sys/mman.h>
  19. #include <fcntl.h>
  20. #include <stddef.h>
  21. #include <unistd.h>
  22. #include <dirent.h>
  23. #include <errno.h>
  24. #include <stdio.h>
  25. #include <limits.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <cppcms/cstdint.h>
  29. #include "posix_util.h"
  30. namespace cppcms {
  31. namespace sessions {
  32. struct session_file_storage::_data {};
  33. using namespace cppcms::impl;
  34. session_file_storage::session_file_storage(std::string path,int concurrency_hint,int procs_no,bool force_flock) :
  35. memory_(MAP_FAILED)
  36. {
  37. if(path.empty()){
  38. if(::getenv("TEMP"))
  39. path_=std::string(::getenv("TEMP")) + "/cppcms_sessions";
  40. else if(::getenv("TMP"))
  41. path_=std::string(::getenv("TMP")) + "/cppcms_sessions";
  42. else
  43. path_ = "/tmp/cppcms_sessions";
  44. }
  45. else
  46. path_=path;
  47. if(::mkdir(path_.c_str(),0777) < 0) {
  48. if(errno!=EEXIST) {
  49. int err=errno;
  50. throw cppcms_error(err,"Failed to create a directory for session storage " + path_);
  51. }
  52. }
  53. lock_size_ = concurrency_hint;
  54. if(force_flock || (procs_no > 1 && !test_pthread_mutex_pshared()))
  55. file_lock_=true;
  56. else
  57. file_lock_=false;
  58. if(!file_lock_ && procs_no > 1) {
  59. #ifdef MAP_ANONYMOUS
  60. int flags = MAP_ANONYMOUS | MAP_SHARED;
  61. #else // defined(MAP_ANON)
  62. int flags = MAP_ANON | MAP_SHARED;
  63. #endif
  64. memory_ = ::mmap(0,sizeof(pthread_mutex_t) * lock_size_,PROT_READ | PROT_WRITE,flags,-1,0);
  65. if(memory_ == MAP_FAILED)
  66. throw cppcms_error(errno,"Memory map failed:");
  67. locks_ = reinterpret_cast<pthread_mutex_t *>(memory_);
  68. for(unsigned i=0;i<lock_size_;i++)
  69. create_mutex(locks_+i,true);
  70. }
  71. else {
  72. mutexes_.resize(lock_size_);
  73. locks_ = &mutexes_.front();
  74. for(unsigned i=0;i<lock_size_;i++)
  75. create_mutex(locks_+i,false);
  76. }
  77. }
  78. session_file_storage::~session_file_storage()
  79. {
  80. if(memory_ !=MAP_FAILED) {
  81. for(unsigned i=0;i<lock_size_;i++)
  82. destroy_mutex(reinterpret_cast<pthread_mutex_t *>(memory_) + i);
  83. munmap((char*)memory_,sizeof(pthread_mutex_t) * lock_size_);
  84. }
  85. else {
  86. for(unsigned i=0;i<lock_size_;i++)
  87. destroy_mutex(&mutexes_[i]);
  88. }
  89. }
  90. pthread_mutex_t *session_file_storage::sid_to_pos(std::string const &sid)
  91. {
  92. char buf[5] = { sid[0],sid[1],sid[2],sid[3],0};
  93. unsigned pos;
  94. sscanf(buf,"%x",&pos);
  95. return locks_ + (pos % lock_size_);
  96. }
  97. void session_file_storage::lock(std::string const &sid)
  98. {
  99. pthread_mutex_lock(sid_to_pos(sid));
  100. }
  101. void session_file_storage::unlock(std::string const &sid)
  102. {
  103. pthread_mutex_unlock(sid_to_pos(sid));
  104. }
  105. std::string session_file_storage::file_name(std::string const &sid)
  106. {
  107. return path_ + "/" + sid;
  108. }
  109. class session_file_storage::locked_file {
  110. public:
  111. locked_file(session_file_storage *object,std::string sid,bool create) :
  112. object_(object),
  113. sid_(sid),
  114. fd_(-1)
  115. {
  116. name_=object_->file_name(sid);
  117. object_->lock(sid_);
  118. for(;;) {
  119. if(create)
  120. fd_=::open(name_.c_str(),O_CREAT | O_RDWR,0666);
  121. else
  122. fd_=::open(name_.c_str(),O_RDWR);
  123. if(fd_ < 0) return;
  124. if(!object_->file_lock_)
  125. return;
  126. struct flock lock;
  127. memset(&lock,0,sizeof(lock));
  128. lock.l_type=F_WRLCK;
  129. lock.l_whence=SEEK_SET;
  130. int res;
  131. while((res=fcntl(fd_,F_SETLKW,&lock)!=0) && errno==EINTR)
  132. ;
  133. if(res < 0) {
  134. ::close(fd_);
  135. fd_=-1;
  136. }
  137. struct stat s_id,s_name;
  138. if(::stat(name_.c_str(),&s_name) < 0) {
  139. // looks like file was deleted
  140. ::close(fd_);
  141. fd_=-1;
  142. continue;
  143. }
  144. if(::fstat(fd_,&s_id) < 0) {
  145. ::close(fd_);
  146. fd_=-1;
  147. return;
  148. }
  149. if(s_id.st_ino!=s_name.st_ino || s_id.st_dev!=s_name.st_dev) {
  150. ::close(fd_);
  151. fd_=-1;
  152. continue;
  153. }
  154. return;
  155. }
  156. }
  157. ~locked_file()
  158. {
  159. if(fd_>=0) {
  160. if(object_->file_lock_) {
  161. struct flock lock;
  162. memset(&lock,0,sizeof(lock));
  163. lock.l_type=F_UNLCK;
  164. lock.l_whence=SEEK_SET;
  165. int res;
  166. while((res=fcntl(fd_,F_SETLKW,&lock)!=0) && errno==EINTR)
  167. ;
  168. }
  169. ::close(fd_);
  170. }
  171. object_->unlock(sid_);
  172. }
  173. int fd() { return fd_; }
  174. std::string name() { return name_; }
  175. private:
  176. session_file_storage *object_;
  177. std::string sid_;
  178. int fd_;
  179. std::string name_;
  180. };
  181. void session_file_storage::save(std::string const &sid,time_t timeout,std::string const &in)
  182. {
  183. locked_file file(this,sid,true);
  184. int fd=file.fd();
  185. if(fd<0)
  186. throw cppcms_error(errno,"failed to create session file");
  187. save_to_file(fd,timeout,in);
  188. }
  189. bool session_file_storage::load(std::string const &sid,time_t &timeout,std::string &out)
  190. {
  191. locked_file file(this,sid,false);
  192. int fd=file.fd();
  193. if(fd<0) return false;
  194. if(!read_from_file(fd,timeout,out)) {
  195. ::unlink(file.name().c_str());
  196. return false;
  197. }
  198. return true;
  199. }
  200. void session_file_storage::remove(std::string const &sid)
  201. {
  202. locked_file file(this,sid,false);
  203. if(file.fd() >= 0)
  204. ::unlink(file.name().c_str());
  205. }
  206. bool session_file_storage::read_timestamp(int fd)
  207. {
  208. ::lseek(fd,0,SEEK_SET);
  209. int64_t stamp;
  210. if(!read_all(fd,&stamp,sizeof(stamp)) || stamp < ::time(0))
  211. return false;
  212. return true;
  213. }
  214. bool session_file_storage::is_blocking()
  215. {
  216. return file_lock_;
  217. }
  218. bool session_file_storage::read_from_file(int fd,time_t &timeout,std::string &data)
  219. {
  220. int64_t f_timeout;
  221. uint32_t crc;
  222. uint32_t size;
  223. ::lseek(fd,0,SEEK_SET);
  224. if(!read_all(fd,&f_timeout,sizeof(f_timeout)))
  225. return false;
  226. if(f_timeout < time(0))
  227. return false;
  228. if(!read_all(fd,&crc,sizeof(crc)) || !read_all(fd,&size,sizeof(size)))
  229. return false;
  230. std::vector<char> buffer(size,0);
  231. impl::crc32_calc crc_calc;
  232. if(size > 0) {
  233. if(!read_all(fd,&buffer.front(),size))
  234. return false;
  235. crc_calc.process_bytes(&buffer.front(),size);
  236. }
  237. uint32_t real_crc=crc_calc.checksum();
  238. if(crc != real_crc)
  239. return false;
  240. timeout=f_timeout;
  241. if(size > 0)
  242. data.assign(&buffer.front(),size);
  243. else
  244. data.clear();
  245. return true;
  246. }
  247. void session_file_storage::save_to_file(int fd,time_t timeout,std::string const &in)
  248. {
  249. struct {
  250. int64_t timeout;
  251. uint32_t crc;
  252. uint32_t size;
  253. } tmp = { timeout, 0, static_cast<uint32_t>(in.size()) };
  254. impl::crc32_calc crc_calc;
  255. crc_calc.process_bytes(in.data(),in.size());
  256. tmp.crc=crc_calc.checksum();
  257. if(!write_all(fd,&tmp,sizeof(tmp)) || !write_all(fd,in.data(),in.size()))
  258. throw cppcms_error(errno,"Failed to write to file");
  259. }
  260. bool session_file_storage::write_all(int fd,void const *vbuf,int n)
  261. {
  262. char const *buf=reinterpret_cast<char const *>(vbuf);
  263. while(n > 0) {
  264. int res = ::write(fd,buf,n);
  265. if(res < 0 && errno==EINTR)
  266. continue;
  267. if(res <= 0)
  268. return false;
  269. n-=res;
  270. }
  271. return true;
  272. }
  273. bool session_file_storage::read_all(int fd,void *vbuf,int n)
  274. {
  275. char *buf=reinterpret_cast<char *>(vbuf);
  276. while(n > 0) {
  277. int res = ::read(fd,buf,n);
  278. if(res < 0 && errno==EINTR)
  279. continue;
  280. if(res <= 0)
  281. return false;
  282. n-=res;
  283. }
  284. return true;
  285. }
  286. void session_file_storage::gc()
  287. {
  288. DIR *d=0;
  289. struct dirent *entry_st=0,*entry_p;
  290. int path_len=pathconf(path_.c_str(),_PC_NAME_MAX);
  291. if(path_len < 0 ) {
  292. // Only "sessions" should be in this directory
  293. // also this directory has high level of trust
  294. // thus... Don't care about symlink exploits
  295. #ifdef NAME_MAX
  296. path_len=NAME_MAX;
  297. #elif defined(PATH_MAX)
  298. path_len=PATH_MAX;
  299. #else
  300. path_len=4096; // guess
  301. #endif
  302. }
  303. // this is for Solaris...
  304. entry_st=(struct dirent *)new char[sizeof(struct dirent)+path_len+1];
  305. try{
  306. if((d=::opendir(path_.c_str()))==NULL) {
  307. int err=errno;
  308. throw cppcms_error(err,"Failed to open directory :"+path_);
  309. }
  310. while(::readdir_r(d,entry_st,&entry_p)==0 && entry_p!=NULL) {
  311. int i;
  312. for(i=0;i<32;i++) {
  313. if(!isxdigit(entry_st->d_name[i]))
  314. break;
  315. }
  316. if(i!=32 || entry_st->d_name[i]!=0)
  317. continue;
  318. std::string sid=entry_st->d_name;
  319. {
  320. locked_file file(this,sid,false);
  321. if(file.fd() >=0 && !read_timestamp(file.fd()))
  322. ::unlink(file.name().c_str());
  323. }
  324. }
  325. ::closedir(d);
  326. }
  327. catch(...) {
  328. if(d) ::closedir(d);
  329. delete [] entry_st;
  330. throw;
  331. }
  332. delete [] entry_st;
  333. }
  334. struct session_file_storage_factory::_data {};
  335. session_file_storage_factory::session_file_storage_factory(std::string path,int conc,int proc_no,bool force_lock) :
  336. storage_(new session_file_storage(path,conc,proc_no,force_lock))
  337. {
  338. }
  339. session_file_storage_factory::~session_file_storage_factory()
  340. {
  341. }
  342. booster::shared_ptr<session_storage> session_file_storage_factory::get()
  343. {
  344. return storage_;
  345. }
  346. bool session_file_storage_factory::requires_gc()
  347. {
  348. return true;
  349. }
  350. void session_file_storage_factory::gc_job()
  351. {
  352. storage_->gc();
  353. }
  354. } // sessions
  355. } // cppcms