diff --git a/configure.ac b/configure.ac index 7e9912f..f4b8ae6 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(s3fs, 1.62) +AC_INIT(s3fs, 1.63) AC_CANONICAL_SYSTEM diff --git a/doc/man/s3fs.1 b/doc/man/s3fs.1 index 524131a..83cb593 100644 --- a/doc/man/s3fs.1 +++ b/doc/man/s3fs.1 @@ -77,6 +77,9 @@ time to wait between read/write activity before giving up. \fB\-o\fR max_stat_cache_size (default="10000" entries (about 4MB)) maximum number of entries in the stat cache .TP +\fB\-o\fR stat_cache_expire (default is no expire) +specify expire time(seconds) for entries in the stat cache +.TP \fB\-o\fR url (default="http://s3.amazonaws.com") sets the url to use to access Amazon S3. If you want to use HTTPS, then you can set url=https://s3.amazonaws.com .TP diff --git a/src/cache.cpp b/src/cache.cpp index c3e4e52..eb36f23 100644 --- a/src/cache.cpp +++ b/src/cache.cpp @@ -36,22 +36,35 @@ static stat_cache_t stat_cache; pthread_mutex_t stat_cache_lock; int get_stat_cache_entry(const char *path, struct stat *buf) { + int is_delete_cache = 0; + pthread_mutex_lock(&stat_cache_lock); stat_cache_t::iterator iter = stat_cache.find(path); if(iter != stat_cache.end()) { - if(foreground) - cout << " stat cache hit [path=" << path << "]" - << " [hit count=" << (*iter).second.hit_count << "]" << endl; + if(!is_stat_cache_expire_time || ((*iter).second.cache_date + stat_cache_expire_time) >= time(NULL)){ + // hit + if(foreground) + cout << " stat cache hit [path=" << path << "]" + << " [time=" << (*iter).second.cache_date << "]" + << " [hit count=" << (*iter).second.hit_count << "]" << endl; - if(buf != NULL) - *buf = (*iter).second.stbuf; + if(buf != NULL) + *buf = (*iter).second.stbuf; - (*iter).second.hit_count++; - pthread_mutex_unlock(&stat_cache_lock); - return 0; + (*iter).second.hit_count++; + pthread_mutex_unlock(&stat_cache_lock); + return 0; + }else{ + // timeout + is_delete_cache = 1; + } } pthread_mutex_unlock(&stat_cache_lock); + if(is_delete_cache){ + delete_stat_cache_entry(path); + } + return -1; } @@ -67,6 +80,7 @@ void add_stat_cache_entry(const char *path, struct stat *st) { pthread_mutex_lock(&stat_cache_lock); stat_cache[path].stbuf = *st; + stat_cache[path].cache_date = time(NULL); // Set time. pthread_mutex_unlock(&stat_cache_lock); } diff --git a/src/cache.h b/src/cache.h index 91e38a6..3ff0e1e 100644 --- a/src/cache.h +++ b/src/cache.h @@ -8,12 +8,15 @@ struct stat_cache_entry { struct stat stbuf; unsigned long hit_count; + time_t cache_date; - stat_cache_entry() : hit_count(0) {} + stat_cache_entry() : hit_count(0), cache_date(0) {} }; extern bool foreground; extern unsigned long max_stat_cache_size; +extern time_t stat_cache_expire_time; +extern int is_stat_cache_expire_time; int get_stat_cache_entry(const char *path, struct stat *buf); void add_stat_cache_entry(const char *path, struct stat *st); diff --git a/src/s3fs.cpp b/src/s3fs.cpp index 5c5cd2c..d89b4c7 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -145,8 +145,7 @@ blkcnt_t get_blocks(off_t size) { return size / 512 + 1; } -static int insert_object(char *name, struct s3_object **head) { - size_t n_len = strlen(name) + 1; +static int insert_object(const char *name, struct s3_object **head) { struct s3_object *new_object; new_object = (struct s3_object *) malloc(sizeof(struct s3_object)); @@ -155,15 +154,12 @@ static int insert_object(char *name, struct s3_object **head) { exit(EXIT_FAILURE); } - new_object->name = (char *) malloc(n_len); - if(new_object->name == NULL) { + if(NULL == (new_object->name = strdup(name))){ free(new_object); printf("insert_object: could not allocate memory\n"); exit(EXIT_FAILURE); } - strncpy(new_object->name, name, n_len); - if((*head) == NULL) new_object->next = NULL; else @@ -324,6 +320,241 @@ static int mkdirp(const string& path, mode_t mode) { return 0; } +// get user name from uid +static string get_username(uid_t uid) +{ + struct passwd* ppw; + if(NULL == (ppw = getpwuid(uid)) || NULL == ppw->pw_name){ + if(foreground){ + printf(" could not get username(errno=%d).\n", (int)errno); + } + if(debug){ + syslog(LOG_DEBUG, "could not get username(errno=%d).\n", (int)errno); + } + return NULL; + } + return string(ppw->pw_name); +} + +// check uid in group(gid) +static int is_uid_inculde_group(uid_t uid, gid_t gid) +{ + static size_t maxlen = 0; // set onece + int result; + char* pbuf; + struct group ginfo; + struct group* pginfo = NULL; + + // make buffer + if(0 == maxlen){ + if(0 > (maxlen = (size_t)sysconf(_SC_GETGR_R_SIZE_MAX))){ + if(foreground){ + printf(" could not get max name length.\n"); + } + if(debug){ + syslog(LOG_DEBUG, "could not get max name length.\n"); + } + maxlen = 0; + return -ERANGE; + } + } + if(NULL == (pbuf = (char*)malloc(sizeof(char) * maxlen))){ + if(foreground){ + printf(" failed to allocate memory.\n"); + } + syslog(LOG_ERR, "failed to allocate memory.\n"); + return -ENOMEM; + } + // get group infomation + if(0 != (result = getgrgid_r(gid, &ginfo, pbuf, maxlen, &pginfo))){ + if(foreground){ + printf(" could not get group infomation.\n"); + } + if(debug){ + syslog(LOG_DEBUG, "could not get group infomation.\n"); + } + free(pbuf); + return -result; + } + + // check group + if(NULL == pginfo){ + // there is not gid in group. + free(pbuf); + return -EINVAL; + } + + string username = get_username(uid); + + char** ppgr_mem; + for(ppgr_mem = pginfo->gr_mem; ppgr_mem && *ppgr_mem; ppgr_mem++){ + if(username == *ppgr_mem){ + // Found username in group. + free(pbuf); + return 1; + } + } + free(pbuf); + return 0; +} + +// +// Get object attributes with stat cache. +// This function is base for s3fs_getattr(). +// +static int get_object_attribute(const char *path, struct stat *stbuf) { + int result; + headers_t meta; + char *s3_realpath; + +//if(foreground) +// printf(" get_object_attribute[path=%s]\n", path); + + memset(stbuf, 0, sizeof(struct stat)); + if(strcmp(path, "/") == 0) { + stbuf->st_nlink = 1; // see fuse faq + stbuf->st_mode = root_mode | S_IFDIR; + return 0; + } + + if(get_stat_cache_entry(path, stbuf) == 0) { + return 0; + } + + s3_realpath = get_realpath(path); + if((result = curl_get_headers(s3_realpath, meta)) != 0) { + free(s3_realpath); + return result; + } + free(s3_realpath); + + stbuf->st_nlink = 1; // see fuse faq + stbuf->st_mtime = get_mtime(meta["x-amz-meta-mtime"].c_str()); + if(stbuf->st_mtime == 0){ + struct tm tm; + strptime(meta["Last-Modified"].c_str(), "%a, %d %b %Y %H:%M:%S %Z", &tm); + stbuf->st_mtime = mktime(&tm); // GMT + } + stbuf->st_mode = get_mode(meta["x-amz-meta-mode"].c_str()); + if(strstr(meta["Content-Type"].c_str(), "x-directory")) + stbuf->st_mode |= S_IFDIR; + else + stbuf->st_mode |= S_IFREG; + + stbuf->st_size = get_size(meta["Content-Length"].c_str()); + + if(S_ISREG(stbuf->st_mode)) + stbuf->st_blocks = get_blocks(stbuf->st_size); + + stbuf->st_uid = get_uid(meta["x-amz-meta-uid"].c_str()); + stbuf->st_gid = get_gid(meta["x-amz-meta-gid"].c_str()); + + add_stat_cache_entry(path, stbuf); + + return 0; +} + +// +// Check the object uid and gid for write/read/execute. +// The param "mask" is as same as access() function. +// If there is not a target file, this function returns -ENOENT. +// If the target file can be accessed, the result always is 0. +// +// path: the target object path +// mask: bit field(F_OK, R_OK, W_OK, X_OK) like access(). +// stat: NULL or the pointer of struct stat. +// +static int check_object_access(const char *path, int mask, struct stat* pstbuf) +{ + int result; + struct stat st; + struct stat* pst = (pstbuf ? pstbuf : &st); + struct fuse_context* pcxt; + +//if(foreground) +// printf(" check_object_access[path=%s]\n", path); + + if(NULL == (pcxt = fuse_get_context())){ + return -EIO; + } + + memset(pst, 0, sizeof(struct stat)); + if(0 != (result = get_object_attribute(path, pst))){ + // If there is not tha target file(object), reusult is -ENOENT. + return result; + } + + if(0 == pcxt->uid){ + // root is allowed all accessing. + return 0; + } + if(F_OK == mask){ + // if there is a file, always return allowed. + return 0; + } + + // compare file mode and uid/gid + mask. + mode_t mode = pst->st_mode; + mode_t base_mask = 0; + if(pcxt->uid == pst->st_uid){ + base_mask = S_IRWXU; + }else if(pcxt->gid == pst->st_gid){ + base_mask = S_IRWXG; + }else{ + if(1 == is_uid_inculde_group(pcxt->uid, pst->st_gid)){ + base_mask = S_IRWXG; + }else{ + base_mask = S_IRWXO; + } + } + mode &= base_mask; + + if(X_OK == (mask & X_OK)){ + if(0 == (mode & (S_IXUSR | S_IXGRP | S_IXOTH))){ + return -EPERM; + } + } + if(W_OK == (mask & W_OK)){ + if(0 == (mode & (S_IWUSR | S_IWGRP | S_IWOTH))){ + return -EACCES; + } + } + if(R_OK == (mask & R_OK)){ + if(0 == (mode & (S_IRUSR | S_IRGRP | S_IROTH))){ + return -EACCES; + } + } + if(0 == mode){ + return -EACCES; + } + return 0; +} + +// +// Check accessing the parent directories of the object by uid and gid. +// +static int check_parent_object_access(const char *path, int mask) +{ + string parent; + int result; + +//if(foreground) +// printf(" check_parent_object_access[path=%s]\n", path); + + for(parent = mydirname(path); 0 < parent.size(); parent = mydirname(parent.c_str())){ + if(parent == "."){ + parent = "/"; + } + if(0 != (result = check_object_access(parent.c_str(), mask, NULL))){ + return result; + } + if(parent == "/" || parent == "."){ + break; + } + } + return 0; +} + // Get fd in mapping data by path static int get_opened_fd(const char* path) { @@ -362,13 +593,13 @@ int get_local_fd(const char* path) { resource = urlEncode(service_path + bucket + s3_realpath); url = host + resource; - if(use_cache.size() > 0) { - result = curl_get_headers(s3_realpath, responseHeaders); - if(result != 0) { - free(s3_realpath); - return -result; - } + result = curl_get_headers(s3_realpath, responseHeaders); + if(result != 0) { + free(s3_realpath); + return -result; + } + if(use_cache.size() > 0) { fd = open(cache_path.c_str(), O_RDWR); // ### TODO should really somehow obey flags here if(fd != -1) { if((fstat(fd, &st)) == -1) { @@ -453,12 +684,15 @@ int get_local_fd(const char* path) { if(fd == -1) YIKES(-errno); - if(use_cache.size() > 0 && !S_ISLNK(mode)) { + if(S_ISREG(mode) && !S_ISLNK(mode)) { // make the file's mtime match that of the file on s3 - struct utimbuf n_mtime; - n_mtime.modtime = get_mtime(responseHeaders["x-amz-meta-mtime"].c_str()); - n_mtime.actime = n_mtime.modtime; - if((utime(cache_path.c_str(), &n_mtime)) == -1) { + // if fd is tmpfile, but we force tor set mtime. + struct timeval tv[2]; + tv[0].tv_sec = get_mtime(responseHeaders["x-amz-meta-mtime"].c_str()); + tv[0].tv_usec= 0L; + tv[1].tv_sec = tv[0].tv_sec; + tv[1].tv_usec= 0L; + if(-1 == futimes(fd, tv)){ fclose(f); YIKES(-errno); } @@ -486,7 +720,8 @@ static int put_headers(const char *path, headers_t meta) { cout << " put_headers[path=" << path << "]" << endl; // files larger than 5GB must be modified via the multipart interface - s3fs_getattr(path, &buf); + get_object_attribute(path, &buf); + if(buf.st_size >= FIVE_GB) return(put_multipart_headers(path, meta)); @@ -589,7 +824,8 @@ static int put_multipart_headers(const char *path, headers_t meta) { body.text = (char *)malloc(1); body.size = 0; - s3fs_getattr(path, &buf); + // already checked by check_object_access(), so only get attr. + get_object_attribute(path, &buf); upload_id = initiate_multipart_upload(path, buf.st_size, meta); if(upload_id.size() == 0){ @@ -1388,54 +1624,18 @@ string md5sum(int fd) { return string(md5); } -static int s3fs_getattr(const char *path, struct stat *stbuf) { +static int s3fs_getattr(const char *path, struct stat *stbuf) +{ int result; - headers_t meta; - char *s3_realpath; if(foreground) printf("s3fs_getattr[path=%s]\n", path); - memset(stbuf, 0, sizeof(struct stat)); - if(strcmp(path, "/") == 0) { - stbuf->st_nlink = 1; // see fuse faq - stbuf->st_mode = root_mode | S_IFDIR; - return 0; - } - - if(get_stat_cache_entry(path, stbuf) == 0) { - return 0; - } - - s3_realpath = get_realpath(path); - if((result = curl_get_headers(s3_realpath, meta)) != 0) { - free(s3_realpath); + // check parent directory attribute. + if(0 != (result = check_parent_object_access(path, X_OK))){ return result; } - free(s3_realpath); - - stbuf->st_nlink = 1; // see fuse faq - stbuf->st_mtime = get_mtime(meta["x-amz-meta-mtime"].c_str()); - if(stbuf->st_mtime == 0) - stbuf->st_mtime = get_mtime(meta["Last-Modified"].c_str()); - - stbuf->st_mode = get_mode(meta["x-amz-meta-mode"].c_str()); - if(strstr(meta["Content-Type"].c_str(), "x-directory")) - stbuf->st_mode |= S_IFDIR; - else - stbuf->st_mode |= S_IFREG; - - stbuf->st_size = get_size(meta["Content-Length"].c_str()); - - if(S_ISREG(stbuf->st_mode)) - stbuf->st_blocks = get_blocks(stbuf->st_size); - - stbuf->st_uid = get_uid(meta["x-amz-meta-uid"].c_str()); - stbuf->st_gid = get_gid(meta["x-amz-meta-gid"].c_str()); - - add_stat_cache_entry(path, stbuf); - - return 0; + return check_object_access(path, F_OK, stbuf); } static int s3fs_readlink(const char *path, char *buf, size_t size) { @@ -1541,7 +1741,7 @@ string lookupMimeType(string s) { } // common function for creation of a plain object -static int create_file_object(const char *path, mode_t mode) { +static int create_file_object(const char *path, mode_t mode, uid_t uid, gid_t gid) { int result; char *s3_realpath; CURL *curl = NULL; @@ -1561,10 +1761,10 @@ static int create_file_object(const char *path, mode_t mode) { headers.append("Content-Type: " + contentType); // x-amz headers: (a) alphabetical order and (b) no spaces after colon headers.append("x-amz-acl:" + default_acl); - headers.append("x-amz-meta-gid:" + str(getgid())); + headers.append("x-amz-meta-gid:" + str(gid)); headers.append("x-amz-meta-mode:" + str(mode)); headers.append("x-amz-meta-mtime:" + str(time(NULL))); - headers.append("x-amz-meta-uid:" + str(getuid())); + headers.append("x-amz-meta-uid:" + str(uid)); if(public_bucket.substr(0,1) != "1") headers.append("Authorization: AWS " + AWSAccessKeyId + ":" + calc_signature("PUT", contentType, date, headers.get(), resource)); @@ -1598,11 +1798,24 @@ static int s3fs_mknod(const char *path, mode_t mode, dev_t rdev) static int s3fs_create(const char *path, mode_t mode, struct fuse_file_info *fi) { int result; headers_t meta; + struct fuse_context* pcxt; if(foreground) cout << "s3fs_create[path=" << path << "][mode=" << mode << "]" << "[flags=" << fi->flags << "]" << endl; - result = create_file_object(path, mode); + if(NULL == (pcxt = fuse_get_context())){ + return -EIO; + } + + // check parent directory attribute. + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + result = check_object_access(path, W_OK, NULL); + if(0 != result && -ENOENT != result){ + return result; + } + result = create_file_object(path, mode, pcxt->uid, pcxt->gid); if(result != 0) return result; @@ -1675,10 +1888,28 @@ static int create_directory_object(const char *path, mode_t mode, time_t time, u static int s3fs_mkdir(const char *path, mode_t mode) { + int result; + struct fuse_context* pcxt; + if(foreground) cout << "s3fs_mkdir[path=" << path << "][mode=" << mode << "]" << endl; - return create_directory_object(path, mode, time(NULL), getuid(), getgid()); + if(NULL == (pcxt = fuse_get_context())){ + return -EIO; + } + + // check parent directory attribute. + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(-ENOENT != (result = check_object_access(path, F_OK, NULL))){ + if(0 == result){ + result = -EEXIST; + } + return result; + } + + return create_directory_object(path, mode, time(NULL), pcxt->uid, pcxt->gid); } static int s3fs_unlink(const char *path) { @@ -1688,6 +1919,10 @@ static int s3fs_unlink(const char *path) { if(foreground) printf("s3fs_unlink[path=%s]\n", path); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + s3_realpath = get_realpath(path); result = curl_delete(s3_realpath); @@ -1767,6 +2002,10 @@ static int s3fs_rmdir(const char *path) { if(foreground) printf("s3fs_rmdir [path=%s]\n", path); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + s3_realpath = get_realpath(path); // directory must be empty @@ -1788,6 +2027,16 @@ static int s3fs_symlink(const char *from, const char *to) { if(foreground) cout << "s3fs_symlink[from=" << from << "][to=" << to << "]" << endl; + if(0 != (result = check_parent_object_access(to, W_OK | X_OK))){ + return result; + } + if(-ENOENT != (result = check_object_access(to, F_OK, NULL))){ + if(0 == result){ + result = -EEXIST; + } + return result; + } + headers_t headers; headers["x-amz-meta-mode"] = str(S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO); headers["x-amz-meta-mtime"] = str(time(NULL)); @@ -1831,6 +2080,15 @@ static int rename_object(const char *from, const char *to) { if(debug) syslog(LOG_DEBUG, "rename_object [from=%s] [to=%s]", from, to); + if(0 != (result = check_parent_object_access(to, W_OK | X_OK))){ + // not permmit writing "to" object parent dir. + return result; + } + if(0 != (result = check_parent_object_access(from, W_OK | X_OK))){ + // not permmit removing "from" object parent dir. + return result; + } + s3_realpath = get_realpath(from); result = curl_get_headers(s3_realpath, meta); @@ -1866,6 +2124,15 @@ static int rename_object_nocopy(const char *from, const char *to) { if(debug) syslog(LOG_DEBUG, "rename_object_nocopy [from=%s] [to=%s]", from, to); + if(0 != (result = check_parent_object_access(to, W_OK | X_OK))){ + // not permmit writing "to" object parent dir. + return result; + } + if(0 != (result = check_parent_object_access(from, W_OK | X_OK))){ + // not permmit removing "from" object parent dir. + return result; + } + // Downloading if(0 > (fd = get_opened_fd(from))){ if(0 > (fd = get_local_fd(from))){ @@ -1928,9 +2195,17 @@ static int rename_large_object(const char *from, const char *to) { if(debug) syslog(LOG_DEBUG, "rename_large_object [from=%s] [to=%s]", from, to); - s3fs_getattr(from, &buf); - s3_realpath = get_realpath(from); + if(0 != (result = check_parent_object_access(to, W_OK | X_OK))){ + // not permmit writing "to" object parent dir. + return result; + } + if(0 != (result = check_parent_object_access(from, W_OK | X_OK))){ + // not permmit removing "from" object parent dir. + return result; + } + get_object_attribute(from, &buf); + s3_realpath = get_realpath(from); if((curl_get_headers(s3_realpath, meta) != 0)) { free(s3_realpath); return -1; @@ -2269,7 +2544,15 @@ static int s3fs_rename(const char *from, const char *to) { if(debug) syslog(LOG_DEBUG, "s3fs_rename [from=%s] [to=%s]", from, to); - s3fs_getattr(from, &buf); + if(0 != (result = check_parent_object_access(to, W_OK | X_OK))){ + // not permmit writing "to" object parent dir. + return result; + } + if(0 != (result = check_parent_object_access(from, W_OK | X_OK))){ + // not permmit removing "from" object parent dir. + return result; + } + get_object_attribute(from, &buf); // files larger than 5GB must be modified via the multipart interface if(S_ISDIR(buf.st_mode)){ @@ -2302,6 +2585,13 @@ static int s3fs_chmod(const char *path, mode_t mode) { if(foreground) printf("s3fs_chmod [path=%s] [mode=%d]\n", path, mode); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, W_OK, NULL))){ + return result; + } + s3_realpath = get_realpath(path); result = curl_get_headers(s3_realpath, meta); if(result != 0) { @@ -2332,6 +2622,13 @@ static int s3fs_chmod_nocopy(const char *path, mode_t mode) { if(foreground) printf("s3fs_chmod_nocopy [path=%s] [mode=%d]\n", path, mode); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, W_OK, NULL))){ + return result; + } + // Downloading if(0 > (fd = get_opened_fd(path))){ if(0 > (fd = get_local_fd(path))){ @@ -2392,6 +2689,13 @@ static int s3fs_chown(const char *path, uid_t uid, gid_t gid) { if(foreground) printf("s3fs_chown [path=%s] [uid=%d] [gid=%d]\n", path, uid, gid); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, W_OK, NULL))){ + return result; + } + s3_realpath = get_realpath(path); headers_t meta; result = curl_get_headers(s3_realpath, meta); @@ -2430,6 +2734,13 @@ static int s3fs_chown_nocopy(const char *path, uid_t uid, gid_t gid) { if(foreground) printf("s3fs_chown_nocopy [path=%s] [uid=%d] [gid=%d]\n", path, uid, gid); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, W_OK, NULL))){ + return result; + } + // Downloading if(0 > (fd = get_opened_fd(path))){ if(0 > (fd = get_local_fd(path))){ @@ -2500,6 +2811,13 @@ static int s3fs_truncate(const char *path, off_t size) { if(foreground) printf("s3fs_truncate[path=%s][size=%zd]\n", path, size); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, W_OK, NULL))){ + return result; + } + // Get file information s3_realpath = get_realpath(path); result = curl_get_headers(s3_realpath, meta); @@ -2558,6 +2876,14 @@ static int s3fs_open(const char *path, struct fuse_file_info *fi) { if(foreground) cout << "s3fs_open[path=" << path << "][flags=" << fi->flags << "]" << endl; + int mask = (O_RDONLY != (fi->flags & O_ACCMODE) ? W_OK : R_OK); + if(0 != (result = check_parent_object_access(path, mask | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, mask, NULL))){ + return result; + } + // Go do the truncation if called for if((unsigned int)fi->flags & O_TRUNC) { result = s3fs_truncate(path, 0); @@ -2595,8 +2921,9 @@ static int s3fs_write( const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int res = pwrite(fi->fh, buf, size, offset); - if(foreground) - cout << "s3fs_write[path=" << path << "]" << endl; + // Commented - This message is output too much +//if(foreground) +// cout << "s3fs_write[path=" << path << "]" << endl; if(res == -1) YIKES(-errno); @@ -2631,9 +2958,17 @@ static int s3fs_flush(const char *path, struct fuse_file_info *fi) { if(foreground) cout << "s3fs_flush[path=" << path << "][fd=" << fd << "]" << endl; + int mask = (O_RDONLY != (fi->flags & O_ACCMODE) ? W_OK : R_OK); + if(0 != (result = check_parent_object_access(path, mask | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, mask, NULL))){ + return result; + } + // NOTE- fi->flags is not available here flags = get_flags(fd); - if((flags & O_RDWR) || (flags & O_WRONLY)) { + if(O_RDONLY != (flags & O_ACCMODE)) { headers_t meta; s3_realpath = get_realpath(path); result = curl_get_headers(s3_realpath, meta); @@ -2643,31 +2978,18 @@ static int s3fs_flush(const char *path, struct fuse_file_info *fi) { return result; // if the cached file matches the remote file skip uploading - if(use_cache.size() > 0) { - struct stat st; + struct stat st; + if((fstat(fd, &st)) == -1) + YIKES(-errno); - if((fstat(fd, &st)) == -1) - YIKES(-errno); - - if(str(st.st_size) == meta["Content-Length"] && - (str(st.st_mtime) == meta["x-amz-meta-mtime"])) { - return result; - } + if(str(st.st_size) == meta["Content-Length"] && + (str(st.st_mtime) == meta["x-amz-meta-mtime"])) { + return result; } - // force the cached copy to have the same mtime as the remote copy - if(use_cache.size() > 0) { - struct stat st; - struct utimbuf n_mtime; - string cache_path(use_cache + "/" + bucket + path); - - if((stat(cache_path.c_str(), &st)) == 0) { - n_mtime.modtime = get_mtime(meta["x-amz-meta-mtime"].c_str()); - n_mtime.actime = n_mtime.modtime; - if((utime(cache_path.c_str(), &n_mtime)) == -1) { - YIKES(-errno); - } - } + // If both mtime are not same, force to change mtime based on fd. + if(str(st.st_mtime) != meta["x-amz-meta-mtime"]){ + meta["x-amz-meta-mtime"] = str(st.st_mtime); } return put_local_fd(path, meta, fd); @@ -2738,6 +3060,20 @@ static CURL *create_head_handle(head_data *request_data) { return curl_handle; } +static int s3fs_opendir(const char *path, struct fuse_file_info *fi) +{ + int result; + int mask = (O_RDONLY != (fi->flags & O_ACCMODE) ? W_OK : R_OK) | X_OK; + + if(foreground) + cout << "s3fs_opendir [path=" << path << "][flags=" << fi->flags << "]" << endl; + + if(0 == (result = check_object_access(path, mask, NULL))){ + result = check_parent_object_access(path, mask); + } + return result; +} + static int s3fs_readdir( const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { CURLM *mh; @@ -2753,15 +3089,20 @@ static int s3fs_readdir( if(foreground) cout << "s3fs_readdir[path=" << path << "]" << endl; - // get a list of all the objects int result; + if(0 != (result = check_object_access(path, X_OK, NULL))){ + return result; + } + + // get a list of all the objects if((result = list_bucket(path, &head)) != 0){ if(foreground) printf(" s3fs_readdir list_bucket returns error.\n"); return result; } - if(head == NULL){ + // force to add "." and ".." name. + if(0 != insert_object("..", &head) || 0 != insert_object(".", &head) || !head){ if(foreground) printf(" s3fs_readdir list_bucket returns empty head.\n"); return 0; @@ -3109,8 +3450,10 @@ static int append_objects_from_xml(const char* path, const char *xml, struct s3_ xmlXPathFreeObject(contents_xp); xmlXPathFreeContext(ctx); xmlFreeDoc(doc); + free(name); return -1; } + free(name); }else{ //if(foreground) // printf("append_objects_from_xml name is file or subdir in dir. but continue.\n"); @@ -3161,6 +3504,9 @@ static bool is_truncated(const char *xml) { return false; } +// return: the pointer to object name on allocated memory. +// the pointer to "c_strErrorObjectName".(not allocated) +// NULL(a case of something error occured) static char *get_object_name(xmlDocPtr doc, xmlNodePtr node, const char* path) { // Get full path @@ -3200,11 +3546,11 @@ static char *get_object_name(xmlDocPtr doc, xmlNodePtr node, const char* path) // case of "name" if(0 == strcmp(dirpath, ".")){ // OK - return (char*)mybname; + return strdup(mybname); }else{ if(0 == strcmp(dirpath, basepath)){ // OK - return (char*)mybname; + return strdup(mybname); } } } @@ -3219,7 +3565,7 @@ static int remote_mountpath_exists(const char *path) { printf("remote_mountpath_exists [path=%s]\n", path); // getattr will prefix the path with the remote mountpoint - s3fs_getattr("", &stbuf); + get_object_attribute("", &stbuf); if(!S_ISDIR(stbuf.st_mode)) return -1; @@ -3314,9 +3660,13 @@ static void s3fs_destroy(void*) { static int s3fs_access(const char *path, int mask) { if(foreground) - printf("s3fs_access[path=%s]\n", path); + printf("s3fs_access[path=%s][mask=%s%s%s%s]\n", path, + ((mask & R_OK) == R_OK) ? "R_OK " : "", + ((mask & W_OK) == W_OK) ? "W_OK " : "", + ((mask & X_OK) == X_OK) ? "X_OK " : "", + (mask == F_OK) ? "F_OK" : ""); - return 0; + return check_object_access(path, mask, NULL); } static int s3fs_utimens(const char *path, const struct timespec ts[2]) { @@ -3327,6 +3677,13 @@ static int s3fs_utimens(const char *path, const struct timespec ts[2]) { if(foreground) printf("s3fs_utimens[path=%s][mtime=%zd]\n", path, ts[1].tv_sec); + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, W_OK, NULL))){ + return result; + } + s3_realpath = get_realpath(path); if((result = curl_get_headers(s3_realpath, meta) != 0)) { free(s3_realpath); @@ -3352,6 +3709,13 @@ static int s3fs_utimens_nocopy(const char *path, const struct timespec ts[2]) { if(foreground) cout << "s3fs_utimens_nocopy [path=" << path << "][mtime=" << str(ts[1].tv_sec) << "]" << endl; + if(0 != (result = check_parent_object_access(path, W_OK | X_OK))){ + return result; + } + if(0 != (result = check_object_access(path, W_OK, NULL))){ + return result; + } + // Downloading if(0 > (fd = get_opened_fd(path))){ if(0 > (fd = get_local_fd(path))){ @@ -3926,6 +4290,9 @@ static void show_help (void) { " max_stat_cache_size (default=\"10000\" entries (about 4MB))\n" " - maximum number of entries in the stat cache\n" "\n" + " stat_cache_expire (default is no expire)\n" + " - specify expire time(seconds) for entries in the stat cache.\n" + "\n" " url (default=\"http://s3.amazonaws.com\")\n" " - sets the url to use to access amazon s3\n" "\n" @@ -4131,6 +4498,11 @@ static int my_fuse_opt_proc(void *data, const char *arg, int key, struct fuse_ar max_stat_cache_size = strtoul(strchr(arg, '=') + 1, 0, 10); return 0; } + if (strstr(arg, "stat_cache_expire=") != 0) { + is_stat_cache_expire_time = 1; + stat_cache_expire_time = strtoul(strchr(arg, '=') + 1, 0, 10); + return 0; + } if(strstr(arg, "noxmlns") != 0) { noxmlns = true; return 0; @@ -4377,6 +4749,7 @@ int main(int argc, char *argv[]) { s3fs_oper.statfs = s3fs_statfs; s3fs_oper.flush = s3fs_flush; s3fs_oper.release = s3fs_release; + s3fs_oper.opendir = s3fs_opendir; s3fs_oper.readdir = s3fs_readdir; s3fs_oper.init = s3fs_init; s3fs_oper.destroy = s3fs_destroy; diff --git a/src/s3fs.h b/src/s3fs.h index 9a4ae65..6fcb5c4 100644 --- a/src/s3fs.h +++ b/src/s3fs.h @@ -47,6 +47,8 @@ static mode_t root_mode = 0; static std::string passwd_file = ""; static bool utility_mode = 0; unsigned long max_stat_cache_size = 10000; +time_t stat_cache_expire_time = 0; +int is_stat_cache_expire_time = 0; bool noxmlns = false; bool nocopyapi = false; @@ -104,7 +106,7 @@ uid_t get_uid(const char *s); gid_t get_gid(const char *s); blkcnt_t get_blocks(off_t size); -static int insert_object(char *name, struct s3_object **head); +static int insert_object(const char *name, struct s3_object **head); static unsigned int count_object_list(struct s3_object *list); static int free_object(struct s3_object *object); static int free_object_list(struct s3_object *head); @@ -139,6 +141,7 @@ static int s3fs_write( static int s3fs_statfs(const char *path, struct statvfs *stbuf); static int s3fs_flush(const char *path, struct fuse_file_info *fi); static int s3fs_release(const char *path, struct fuse_file_info *fi); +static int s3fs_opendir(const char *path, struct fuse_file_info *fi); static int s3fs_readdir( const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi); static int s3fs_access(const char *path, int mask);