static int cfs_create(const char *path, mode_t mode, struct fuse_file_info *info) { FILE *temp_file; if (*temp_dir) { char tmp_path[PATH_MAX]; strncpy(tmp_path, path, PATH_MAX); char *pch; while((pch = strchr(tmp_path, '/'))) { *pch = '.'; } char file_path[PATH_MAX]; snprintf(file_path, PATH_MAX, "%s/.cloudfuse%ld-%s", temp_dir, (long)getpid(), tmp_path); temp_file = fopen(file_path, "w+b"); } else temp_file = tmpfile(); openfile *of = (openfile *)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); fclose(temp_file); of->flags = info->flags; info->fh = (uintptr_t)of; update_dir_cache(path, 0, 0, 0); info->direct_io = 1; return 0; }
static int cfs_truncate(const char* path, off_t size) { debugf(DBG_LEVEL_NORM, "cfs_truncate(%s) size=%lu", path, size); cloudfs_object_truncate(path, size); update_dir_cache(path, size, 0, 0); debugf(DBG_LEVEL_NORM, "exit: cfs_truncate(%s)", path); return 0; }
static int cfs_mkdir(const char *path, mode_t mode) { if (cloudfs_create_directory(path)) { update_dir_cache(path, 0, 1, 0); return 0; } return -ENOENT; }
static int cfs_ftruncate(const char *path, off_t size, struct fuse_file_info *info) { openfile *of = (openfile *)(uintptr_t)info->fh; if (ftruncate(of->fd, size)) return -errno; lseek(of->fd, 0, SEEK_SET); update_dir_cache(path, size, 0, 0); return 0; }
static int cfs_symlink(const char *src, const char *dst) { if(cloudfs_create_symlink(src, dst)) { update_dir_cache(dst, 1, 0, 1); return 0; } return -EIO; }
static int cfs_create(const char *path, mode_t mode, struct fuse_file_info *info) { FILE *temp_file = tmpfile(); openfile *of = (openfile *)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); fclose(temp_file); of->flags = info->flags; info->fh = (uintptr_t)of; update_dir_cache(path, 0, 0); info->direct_io = 1; return 0; }
static int cfs_symlink(const char* src, const char* dst) { debugf(DBG_LEVEL_NORM, KBLU"cfs_symlink(%s, %s)", src, dst); if (cloudfs_create_symlink(src, dst)) { update_dir_cache(dst, 1, 0, 1); debugf(DBG_LEVEL_NORM, KBLU"exit0: cfs_symlink(%s, %s)", src, dst); return 0; } debugf(DBG_LEVEL_NORM, KRED"exit1: cfs_symlink(%s, %s) io error", src, dst); return -EIO; }
static int cfs_ftruncate(const char* path, off_t size, struct fuse_file_info* info) { debugf(DBG_LEVEL_NORM, KBLU "cfs_ftruncate(%s) size=%lu", path, size); file_buffer_size = size; openfile* of = (openfile*)(uintptr_t)info->fh; if (ftruncate(of->fd, size)) return -errno; lseek(of->fd, 0, SEEK_SET); update_dir_cache(path, size, 0, 0); debugf(DBG_LEVEL_NORM, KBLU "exit: cfs_ftruncate(%s)", path); return 0; }
static int cfs_mkdir(const char* path, mode_t mode) { debugf(DBG_LEVEL_NORM, KBLU "cfs_mkdir(%s)", path); int response = cloudfs_create_directory(path); if (response) { update_dir_cache(path, 0, 1, 0); debug_list_cache_content(); debugf(DBG_LEVEL_NORM, KBLU "exit 0: cfs_mkdir(%s)", path); return 0; } debugf(DBG_LEVEL_NORM, KRED "exit 1: cfs_mkdir(%s) response=%d", path, response); return -ENOENT; }
static int cfs_rename(const char *src, const char *dst) { dir_entry *src_de = path_info(src); if (!src_de) return -ENOENT; if (src_de->isdir) return -EISDIR; if (cloudfs_copy_object(src, dst)) { /* FIXME this isn't quite right as doesn't preserve last modified */ update_dir_cache(dst, src_de->size, 0, 0); return cfs_unlink(src); } return -EIO; }
static int cfs_write(const char* path, const char* buf, size_t length, off_t offset, struct fuse_file_info* info) { debugf(DBG_LEVEL_EXTALL, KBLU "cfs_write(%s) bufflength=%lu offset=%lu", path, length, offset); // FIXME: Potential inconsistent cache update if pwrite fails? update_dir_cache(path, offset + length, 0, 0); //int result = pwrite(info->fh, buf, length, offset); int result = pwrite(((openfile*)(uintptr_t)info->fh)->fd, buf, length, offset); int errsv = errno; if (errsv == 0) debugf(DBG_LEVEL_EXTALL, KBLU "exit 0: cfs_write(%s) result=%d:%s", path, errsv, strerror(errsv)); else debugf(DBG_LEVEL_EXTALL, KBLU "exit 1: cfs_write(%s) "KRED"result=%d:%s", path, errsv, strerror(errsv)); return result; }
static int cfs_flush(const char *path, struct fuse_file_info *info) { openfile *of = (openfile *)(uintptr_t)info->fh; if (of) { update_dir_cache(path, cloudfs_file_size(of->fd), 0, 0); if (of->flags & O_RDWR || of->flags & O_WRONLY) { FILE *fp = fdopen(dup(of->fd), "r"); rewind(fp); if (!cloudfs_object_read_fp(path, fp)) { fclose(fp); return -ENOENT; } fclose(fp); } } return 0; }
static int cfs_open(const char *path, struct fuse_file_info *info) { FILE *temp_file = tmpfile(); if (!(info->flags & O_WRONLY)) { if (!object_write_fp(path, temp_file)) { fclose(temp_file); return -ENOENT; } update_dir_cache(path, 0, 0); } openfile *of = (openfile *)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); fclose(temp_file); of->flags = info->flags; info->fh = (uintptr_t)of; info->direct_io = 1; return 0; }
static int cfs_rename(const char* src, const char* dst) { debugf(DBG_LEVEL_NORM, KBLU"cfs_rename(%s, %s)", src, dst); dir_entry* src_de = path_info(src); if (!src_de) { debugf(DBG_LEVEL_NORM, KRED"exit 0: cfs_rename(%s,%s) not-found", src, dst); return -ENOENT; } if (src_de->isdir) { debugf(DBG_LEVEL_NORM, KRED"exit 1: cfs_rename(%s,%s) cannot rename dirs!", src, dst); return -EISDIR; } if (cloudfs_copy_object(src, dst)) { /* FIXME this isn't quite right as doesn't preserve last modified */ //fix done in cloudfs_copy_object() update_dir_cache(dst, src_de->size, 0, 0); int result = cfs_unlink(src); dir_entry* dst_de = path_info(dst); if (!dst_de) debugf(DBG_LEVEL_NORM, KRED"cfs_rename(%s,%s) dest-not-found-in-cache", src, dst); else { debugf(DBG_LEVEL_NORM, KBLU"cfs_rename(%s,%s) upload ok", src, dst); //copy attributes, shortcut, rather than forcing a download from cloud copy_dir_entry(src_de, dst_de); } debugf(DBG_LEVEL_NORM, KBLU"exit 3: cfs_rename(%s,%s)", src, dst); return result; } debugf(DBG_LEVEL_NORM, KRED"exit 4: cfs_rename(%s,%s) io error", src, dst); return -EIO; }
static int cfs_write(const char *path, const char *buf, size_t length, off_t offset, struct fuse_file_info *info) { update_dir_cache(path, offset + length, 0, 0); return pwrite(((openfile *)(uintptr_t)info->fh)->fd, buf, length, offset); }
// open (download) file from cloud // todo: implement etag optimisation, download only if content changed, http://www.17od.com/2012/12/19/ten-useful-openstack-swift-features/ static int cfs_open(const char* path, struct fuse_file_info* info) { debugf(DBG_LEVEL_NORM, KBLU "cfs_open(%s)", path); FILE* temp_file = NULL; int errsv; dir_entry* de = path_info(path); if (*temp_dir) { char file_path_safe[NAME_MAX]; get_safe_cache_file_path(path, file_path_safe, temp_dir); debugf(DBG_LEVEL_EXT, "cfs_open: try open (%s)", file_path_safe); if (access(file_path_safe, F_OK) != -1) { // file exists temp_file = fopen(file_path_safe, "r"); errsv = errno; if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED"exit 0: cfs_open can't open temp_file=[%s] err=%d:%s", file_path_safe, errsv, strerror(errsv)); return -ENOENT; } else debugf(DBG_LEVEL_EXT, "cfs_open: file exists"); } else { errsv = errno; debugf(DBG_LEVEL_EXT, "cfs_open: file not in cache, err=%s", strerror(errsv)); //FIXME: commented out as this condition will not be meet in some odd cases and program will crash //if (!(info->flags & O_WRONLY)) { debugf(DBG_LEVEL_EXT, "cfs_open: opening for write"); // we need to lock on the filename another process could open the file // while we are writing to it and then only read part of the file // duplicate the directory caching datastructure to make the code easier // to understand. // each file in the cache needs: // filename, is_writing, last_closed, is_removing // the first time a file is opened a new entry is created in the cache // setting the filename and is_writing to true. This check needs to be // wrapped with a lock. // // each time a file is closed we set the last_closed for the file to now // and we check the cache for files whose last // closed is greater than cache_timeout, then start a new thread rming // that file. // TODO: just to prevent this craziness for now temp_file = fopen(file_path_safe, "w+b"); errsv = errno; if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED"exit 1: cfs_open cannot open temp_file=[%s] err=%d:%s", file_path_safe, errsv, strerror(errsv)); return -ENOENT; } if (!cloudfs_object_write_fp(path, temp_file)) { fclose(temp_file); debugf(DBG_LEVEL_NORM, KRED "exit 2: cfs_open(%s) cannot download/write", path); return -ENOENT; } } } else { temp_file = tmpfile(); if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED"exit 3: cfs_open cannot create temp_file err=%s", strerror(errno)); return -ENOENT; } if (!(info->flags & O_TRUNC)) { if (!cloudfs_object_write_fp(path, temp_file) && !(info->flags & O_CREAT)) { fclose(temp_file); debugf(DBG_LEVEL_NORM, KRED"exit 4: cfs_open(%s) cannot download/write", path); return -ENOENT; } } } update_dir_cache(path, (de ? de->size : 0), 0, 0); openfile* of = (openfile*)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); if (of->fd == -1) { //FIXME: potential leak if free not used? free(of); debugf(DBG_LEVEL_NORM, KRED "exit 5: cfs_open(%s) of->fd", path); return -ENOENT; } fclose(temp_file); //TODO: why this allocation to of? of->flags = info->flags; info->fh = (uintptr_t)of; info->direct_io = 1; info->nonseekable = 0; //FIXME: potential leak if free(of) not used? although if free(of) is used will generate bad descriptor errors debugf(DBG_LEVEL_NORM, KBLU "exit 6: cfs_open(%s)", path); return 0; }
static int cfs_open(const char *path, struct fuse_file_info *info) { FILE *temp_file; dir_entry *de = path_info(path); if (*temp_dir) { char tmp_path[PATH_MAX]; strncpy(tmp_path, path, PATH_MAX); char *pch; while((pch = strchr(tmp_path, '/'))) { *pch = '.'; } char file_path[PATH_MAX]; snprintf(file_path, PATH_MAX, "%s/.cloudfuse%ld-%s", temp_dir, (long)getpid(), tmp_path); if(access(file_path, F_OK) != -1) { // file exists temp_file = fopen(file_path, "r"); } else if (!(info->flags & O_WRONLY)) { // we need to lock on the filename another process could open the file // while we are writing to it and then only read part of the file // duplicate the directory caching datastructure to make the code easier // to understand. // each file in the cache needs: // filename, is_writing, last_closed, is_removing // the first time a file is opened a new entry is created in the cache // setting the filename and is_writing to true. This check needs to be // wrapped with a lock. // // each time a file is closed we set the last_closed for the file to now // and we check the cache for files whose last // closed is greater than cache_timeout, then start a new thread rming // that file. // TODO: just to prevent this craziness for now temp_file = fopen(file_path, "w+b"); if (!cloudfs_object_write_fp(path, temp_file)) { fclose(temp_file); return -ENOENT; } } } else { temp_file = tmpfile(); if (!(info->flags & O_WRONLY)) { if (!cloudfs_object_write_fp(path, temp_file)) { fclose(temp_file); return -ENOENT; } } } update_dir_cache(path, (de ? de->size : 0), 0, 0); openfile *of = (openfile *)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); if (of->fd == -1) return -ENOENT; fclose(temp_file); of->flags = info->flags; info->fh = (uintptr_t)of; info->direct_io = 1; return 0; }
//todo: flush will upload a file again even if just file attributes are changed. //optimisation needed to detect if content is changed and to only save meta when just attribs are modified. static int cfs_flush(const char* path, struct fuse_file_info* info) { debugf(DBG_LEVEL_NORM, KBLU "cfs_flush(%s)", path); debug_print_descriptor(info); openfile* of = (openfile*)(uintptr_t)info->fh; int errsv = 0; if (of) { // get the actual file size and truncate it. This ensures that if the new file is smaller // than the previous one, the proper size is uploaded. struct stat stbuf; cfs_getattr(path, &stbuf); update_dir_cache(path, stbuf.st_size, 0, 0); ftruncate(of->fd, stbuf.st_size); if (of->flags & O_RDWR || of->flags & O_WRONLY) { FILE* fp = fdopen(dup(of->fd), "r"); errsv = errno; if (fp != NULL) { rewind(fp); //calculate md5 hash, compare with cloud hash to determine if file content is changed char md5_file_hash_str[MD5_DIGEST_HEXA_STRING_LEN] = "\0"; file_md5(fp, md5_file_hash_str); dir_entry* de = check_path_info(path); if (de && de->md5sum != NULL && (!strcasecmp(md5_file_hash_str, de->md5sum))) { //file content is identical, no need to upload entire file, just update metadata debugf(DBG_LEVEL_NORM, KBLU "cfs_flush(%s): skip full upload as content did not change", path); cloudfs_update_meta(de); } else { rewind(fp); debugf(DBG_LEVEL_NORM, KBLU "cfs_flush(%s): perform full upload as content changed (or no file found in cache)", path); if (!cloudfs_object_read_fp(path, fp)) { fclose(fp); errsv = errno; debugf(DBG_LEVEL_NORM, KRED"exit 0: cfs_flush(%s) result=%d:%s", path, errsv, strerror(errno)); return -ENOENT; } } fclose(fp); errsv = errno; } else debugf(DBG_LEVEL_EXT, KRED "status: cfs_flush, err=%d:%s", errsv, strerror(errno)); } } debugf(DBG_LEVEL_NORM, KBLU "exit 1: cfs_flush(%s) result=%d:%s", path, errsv, strerror(errno)); return 0; }
static int cfs_create(const char* path, mode_t mode, struct fuse_file_info* info) { debugf(DBG_LEVEL_NORM, KBLU "cfs_create(%s)", path); FILE* temp_file; int errsv; char file_path_safe[NAME_MAX] = ""; if (*temp_dir) { get_safe_cache_file_path(path, file_path_safe, temp_dir); temp_file = fopen(file_path_safe, "w+b"); errsv = errno; if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED "exit 0: cfs_create cannot open temp file %s.error %s\n", file_path_safe, strerror(errsv)); return -EIO; } } else { temp_file = tmpfile(); errsv = errno; if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED "exit 1: cfs_create cannot open tmp file for path %s.error %s\n", path, strerror(errsv)); return -EIO; } } openfile* of = (openfile*)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); fclose(temp_file); of->flags = info->flags; info->fh = (uintptr_t)of; update_dir_cache(path, 0, 0, 0); info->direct_io = 1; dir_entry* de = check_path_info(path); if (de) { debugf(DBG_LEVEL_EXT, KCYN"cfs_create(%s): found in cache", path); struct timespec now; clock_gettime(CLOCK_REALTIME, &now); debugf(DBG_LEVEL_EXT, KCYN"cfs_create(%s) set utimes as now", path); de->atime.tv_sec = now.tv_sec; de->atime.tv_nsec = now.tv_nsec; de->mtime.tv_sec = now.tv_sec; de->mtime.tv_nsec = now.tv_nsec; de->ctime.tv_sec = now.tv_sec; de->ctime.tv_nsec = now.tv_nsec; char time_str[TIME_CHARS] = ""; get_timespec_as_str(&(de->atime), time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_create: atime=[%s]", time_str); get_timespec_as_str(&(de->mtime), time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_create: mtime=[%s]", time_str); get_timespec_as_str(&(de->ctime), time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_create: ctime=[%s]", time_str); //set chmod & chown de->chmod = mode; de->uid = geteuid(); de->gid = getegid(); } else debugf(DBG_LEVEL_EXT, KBLU "cfs_create(%s) "KYEL"dir-entry not found", path); debugf(DBG_LEVEL_NORM, KBLU "exit 2: cfs_create(%s)=(%s) result=%d:%s", path, file_path_safe, errsv, strerror(errsv)); return 0; }