/
zOMGdev
/
cppcms
Обзор
Документация
Войти
/
zOMGdev
/
cppcms
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/session_posix_file_storage.cpp
392 строки
9 KB
Artyom Beilis
Merged different fixes from v1.0 branch
29 дек 2015, 23:51
29 дек 2015, 23:51
25c44f2
Код
Авторство
О чём код?
/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #if defined(__sun) #define _POSIX_PTHREAD_SEMANTICS #endif #include "session_posix_file_storage.h" #include <cppcms/cppcms_error.h> #include <cppcms/config.h> #include "crc32.h" #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> #include <stddef.h> #include <unistd.h> #include <dirent.h> #include <errno.h> #include <stdio.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <cppcms/cstdint.h> #include "posix_util.h" namespace cppcms { namespace sessions { struct session_file_storage::_data {}; using namespace cppcms::impl; session_file_storage::session_file_storage(std::string path,int concurrency_hint,int procs_no,bool force_flock) : memory_(MAP_FAILED) { if(path.empty()){ if(::getenv("TEMP")) path_=std::string(::getenv("TEMP")) + "/cppcms_sessions"; else if(::getenv("TMP")) path_=std::string(::getenv("TMP")) + "/cppcms_sessions"; else path_ = "/tmp/cppcms_sessions"; } else path_=path; if(::mkdir(path_.c_str(),0777) < 0) { if(errno!=EEXIST) { int err=errno; throw cppcms_error(err,"Failed to create a directory for session storage " + path_); } } lock_size_ = concurrency_hint; if(force_flock || (procs_no > 1 && !test_pthread_mutex_pshared())) file_lock_=true; else file_lock_=false; if(!file_lock_ && procs_no > 1) { #ifdef MAP_ANONYMOUS int flags = MAP_ANONYMOUS | MAP_SHARED; #else // defined(MAP_ANON) int flags = MAP_ANON | MAP_SHARED; #endif memory_ = ::mmap(0,sizeof(pthread_mutex_t) * lock_size_,PROT_READ | PROT_WRITE,flags,-1,0); if(memory_ == MAP_FAILED) throw cppcms_error(errno,"Memory map failed:"); locks_ = reinterpret_cast<pthread_mutex_t *>(memory_); for(unsigned i=0;i<lock_size_;i++) create_mutex(locks_+i,true); } else { mutexes_.resize(lock_size_); locks_ = &mutexes_.front(); for(unsigned i=0;i<lock_size_;i++) create_mutex(locks_+i,false); } } session_file_storage::~session_file_storage() { if(memory_ !=MAP_FAILED) { for(unsigned i=0;i<lock_size_;i++) destroy_mutex(reinterpret_cast<pthread_mutex_t *>(memory_) + i); munmap((char*)memory_,sizeof(pthread_mutex_t) * lock_size_); } else { for(unsigned i=0;i<lock_size_;i++) destroy_mutex(&mutexes_[i]); } } pthread_mutex_t *session_file_storage::sid_to_pos(std::string const &sid) { char buf[5] = { sid[0],sid[1],sid[2],sid[3],0}; unsigned pos; sscanf(buf,"%x",&pos); return locks_ + (pos % lock_size_); } void session_file_storage::lock(std::string const &sid) { pthread_mutex_lock(sid_to_pos(sid)); } void session_file_storage::unlock(std::string const &sid) { pthread_mutex_unlock(sid_to_pos(sid)); } std::string session_file_storage::file_name(std::string const &sid) { return path_ + "/" + sid; } class session_file_storage::locked_file { public: locked_file(session_file_storage *object,std::string sid,bool create) : object_(object), sid_(sid), fd_(-1) { name_=object_->file_name(sid); object_->lock(sid_); for(;;) { if(create) fd_=::open(name_.c_str(),O_CREAT | O_RDWR,0666); else fd_=::open(name_.c_str(),O_RDWR); if(fd_ < 0) return; if(!object_->file_lock_) return; struct flock lock; memset(&lock,0,sizeof(lock)); lock.l_type=F_WRLCK; lock.l_whence=SEEK_SET; int res; while((res=fcntl(fd_,F_SETLKW,&lock))!=0 && errno==EINTR) ; if(res < 0) { ::close(fd_); fd_=-1; } struct stat s_id,s_name; if(::stat(name_.c_str(),&s_name) < 0) { // looks like file was deleted ::close(fd_); fd_=-1; continue; } if(::fstat(fd_,&s_id) < 0) { ::close(fd_); fd_=-1; return; } if(s_id.st_ino!=s_name.st_ino || s_id.st_dev!=s_name.st_dev) { ::close(fd_); fd_=-1; continue; } return; } } ~locked_file() { if(fd_>=0) { if(object_->file_lock_) { struct flock lock; memset(&lock,0,sizeof(lock)); lock.l_type=F_UNLCK; lock.l_whence=SEEK_SET; int res; while((res=fcntl(fd_,F_SETLKW,&lock))!=0 && errno==EINTR) ; } ::close(fd_); } object_->unlock(sid_); } int fd() { return fd_; } std::string name() { return name_; } private: session_file_storage *object_; std::string sid_; int fd_; std::string name_; }; void session_file_storage::save(std::string const &sid,time_t timeout,std::string const &in) { locked_file file(this,sid,true); int fd=file.fd(); if(fd<0) throw cppcms_error(errno,"failed to create session file"); save_to_file(fd,timeout,in); } bool session_file_storage::load(std::string const &sid,time_t &timeout,std::string &out) { locked_file file(this,sid,false); int fd=file.fd(); if(fd<0) return false; if(!read_from_file(fd,timeout,out)) { ::unlink(file.name().c_str()); return false; } return true; } void session_file_storage::remove(std::string const &sid) { locked_file file(this,sid,false); if(file.fd() >= 0) ::unlink(file.name().c_str()); } bool session_file_storage::read_timestamp(int fd) { ::lseek(fd,0,SEEK_SET); int64_t stamp; if(!read_all(fd,&stamp,sizeof(stamp)) || stamp < ::time(0)) return false; return true; } bool session_file_storage::is_blocking() { return file_lock_; } bool session_file_storage::read_from_file(int fd,time_t &timeout,std::string &data) { int64_t f_timeout; uint32_t crc; uint32_t size; ::lseek(fd,0,SEEK_SET); if(!read_all(fd,&f_timeout,sizeof(f_timeout))) return false; if(f_timeout < time(0)) return false; if(!read_all(fd,&crc,sizeof(crc)) || !read_all(fd,&size,sizeof(size))) return false; std::vector<char> buffer(size,0); impl::crc32_calc crc_calc; if(size > 0) { if(!read_all(fd,&buffer.front(),size)) return false; crc_calc.process_bytes(&buffer.front(),size); } uint32_t real_crc=crc_calc.checksum(); if(crc != real_crc) return false; timeout=f_timeout; if(size > 0) data.assign(&buffer.front(),size); else data.clear(); return true; } void session_file_storage::save_to_file(int fd,time_t timeout,std::string const &in) { struct { int64_t timeout; uint32_t crc; uint32_t size; } tmp = { timeout, 0, static_cast<uint32_t>(in.size()) }; impl::crc32_calc crc_calc; crc_calc.process_bytes(in.data(),in.size()); tmp.crc=crc_calc.checksum(); if(!write_all(fd,&tmp,sizeof(tmp)) || !write_all(fd,in.data(),in.size())) throw cppcms_error(errno,"Failed to write to file"); } bool session_file_storage::write_all(int fd,void const *vbuf,int n) { char const *buf=reinterpret_cast<char const *>(vbuf); while(n > 0) { int res = ::write(fd,buf,n); if(res < 0 && errno==EINTR) continue; if(res <= 0) return false; n-=res; } return true; } bool session_file_storage::read_all(int fd,void *vbuf,int n) { char *buf=reinterpret_cast<char *>(vbuf); while(n > 0) { int res = ::read(fd,buf,n); if(res < 0 && errno==EINTR) continue; if(res <= 0) return false; n-=res; } return true; } void session_file_storage::gc() { DIR *d=0; struct dirent *entry_st=0,*entry_p; int path_len=pathconf(path_.c_str(),_PC_NAME_MAX); if(path_len < 0 ) { // Only "sessions" should be in this directory // also this directory has high level of trust // thus... Don't care about symlink exploits #ifdef NAME_MAX path_len=NAME_MAX; #elif defined(PATH_MAX) path_len=PATH_MAX; #else path_len=4096; // guess #endif } // this is for Solaris... entry_st=(struct dirent *)new char[sizeof(struct dirent)+path_len+1]; try{ if((d=::opendir(path_.c_str()))==NULL) { int err=errno; throw cppcms_error(err,"Failed to open directory :"+path_); } while(::readdir_r(d,entry_st,&entry_p)==0 && entry_p!=NULL) { int i; for(i=0;i<32;i++) { if(!isxdigit(entry_st->d_name[i])) break; } if(i!=32 || entry_st->d_name[i]!=0) continue; std::string sid=entry_st->d_name; { locked_file file(this,sid,false); if(file.fd() >=0 && !read_timestamp(file.fd())) ::unlink(file.name().c_str()); } } ::closedir(d); } catch(...) { if(d) ::closedir(d); delete [] entry_st; throw; } delete [] entry_st; } struct session_file_storage_factory::_data {}; session_file_storage_factory::session_file_storage_factory(std::string path,int conc,int proc_no,bool force_lock) : storage_(new session_file_storage(path,conc,proc_no,force_lock)) { } session_file_storage_factory::~session_file_storage_factory() { } booster::shared_ptr<session_storage> session_file_storage_factory::get() { return storage_; } bool session_file_storage_factory::requires_gc() { return true; } void session_file_storage_factory::gc_job() { storage_->gc(); } } // sessions } // cppcms