/** * Create a file or directory that hides path below branch_rw */ static int do_create_whiteout(const char *path, int branch_rw, enum whiteout mode) { DBG("%s\n", path); char metapath[PATHLEN_MAX]; if (BUILD_PATH(metapath, METADIR, path)) RETURN(-1); // p MUST be without path to branch prefix here! 2 x branch_rw is correct here! // this creates e.g. branch/.unionfs/some_directory path_create_cutlast(metapath, branch_rw, branch_rw); char p[PATHLEN_MAX]; if (BUILD_PATH(p, uopt.branches[branch_rw].path, metapath)) RETURN(-1); strcat(p, HIDETAG); // TODO check length int res; if (mode == WHITEOUT_FILE) { res = open(p, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); if (res == -1) RETURN(-1); res = close(res); } else { res = mkdir(p, S_IRWXU); if (res) USYSLOG(LOG_ERR, "Creating %s failed: %s\n", p, strerror(errno)); } RETURN(res); }
/* * Name: couchdb_flush * Input Args: * handle - handle to the lcb instance * * Description: Flushes the entire cluster */ int couchdb_flush(void) { lcb_error_t ret; int lock_result; lcb_flush_cmd_t *cmd = malloc(sizeof(*cmd)); const lcb_flush_cmd_t *const flush_cmd[] = { cmd }; cmd->version = 0; lock_result = pthread_rwlock_wrlock(&db_rwlock); if (lock_result != 0) { syslog(LOG_ERR, "%s: Failed to obtain writelock. Error = %d\n", __FUNCTION__, errno); return -errno; } ret = lcb_flush(couchdb_handle, NULL, 1, flush_cmd); if (ret != LCB_SUCCESS) { syslog(LOG_ERR, "%s: lcb_flush failed with error %s\n", __FUNCTION__, lcb_strerror(couchdb_handle, ret)); return -ret; } /* Block the thread till this operation completes */ lcb_wait(couchdb_handle); pthread_rwlock_unlock(&db_rwlock); USYSLOG(LOG_INFO, "\n%s: lflushed siuccess\n", __FUNCTION__); return (1); }
/** * initiate the cow-copy action */ int cow_cp(const char *path, int branch_ro, int branch_rw, bool copy_dir) { DBG("%s\n", path); // create the path to the file path_create_cutlast(path, branch_ro, branch_rw); char from[PATHLEN_MAX], to[PATHLEN_MAX]; if (BUILD_PATH(from, uopt.branches[branch_ro].path, path)) RETURN(-ENAMETOOLONG); if (BUILD_PATH(to, uopt.branches[branch_rw].path, path)) RETURN(-ENAMETOOLONG); setlocale(LC_ALL, ""); struct cow cow; cow.uid = getuid(); // Copy the umask for explicit mode setting. cow.umask = umask(0); umask(cow.umask); cow.from_path = from; cow.to_path = to; struct stat buf; lstat(cow.from_path, &buf); cow.stat = &buf; int res; switch (buf.st_mode & S_IFMT) { case S_IFLNK: res = copy_link(&cow); break; case S_IFDIR: if (copy_dir) { res = copy_directory(path, branch_ro, branch_rw); } else { res = path_create(path, branch_ro, branch_rw); } break; case S_IFBLK: case S_IFCHR: res = copy_special(&cow); break; case S_IFIFO: res = copy_fifo(&cow); break; case S_IFSOCK: USYSLOG(LOG_WARNING, "COW of sockets not supported: %s\n", cow.from_path); RETURN(1); default: res = copy_file(&cow); } RETURN(res); }
/** * Set file owner of after an operation, which created a file. */ int set_owner(const char *path) { struct fuse_context *ctx = fuse_get_context(); if (ctx->uid != 0 && ctx->gid != 0) { int res = lchown(path, ctx->uid, ctx->gid); if (res) { USYSLOG(LOG_WARNING, ":%s: Setting the correct file owner failed: %s !\n", __func__, strerror(errno)); RETURN(-errno); } } RETURN(0); }
static void get_cb(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp) { if (error != LCB_SUCCESS) { COUCHDB_LOG("Error getting the entry for Key %"PRId64"", *(uint64_t *)(resp->v.v0.key)); return; } USYSLOG(LOG_INFO, "%s: get cb %s %d\n",__FUNCTION__, (char *)(resp->v.v0.bytes), (int) resp->v.v0.nbytes); memcpy((char *)value, (char *)(resp->v.v0.bytes), resp->v.v0.nbytes); num_bytes = resp->v.v0.nbytes; }
static int unionfs_mknod(const char *path, mode_t mode, dev_t rdev) { DBG("%s\n", path); int i = find_rw_branch_cutlast(path); if (i == -1) RETURN(-errno); char p[PATHLEN_MAX]; if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG); int file_type = mode & S_IFMT; int file_perm = mode & (S_PROT_MASK); int res = -1; if ((file_type) == S_IFREG) { // under FreeBSD, only the super-user can create ordinary files using mknod // Actually this workaround should not be required any more // since we now have the unionfs_create() method // So can we remove it? USYSLOG (LOG_INFO, "deprecated mknod workaround, tell the unionfs-fuse authors if you see this!\n"); res = creat(p, 0); if (res > 0 && close(res) == -1) USYSLOG(LOG_WARNING, "Warning, cannot close file\n"); } else { res = mknod(p, file_type, rdev); } if (res == -1) RETURN(-errno); set_owner(p); // no error check, since creating the file succeeded // NOW, that the file has the proper owner we may set the requested mode chmod(p, file_perm); remove_hidden(path, i); RETURN(0); }
/** * init method * called before first access to the filesystem */ static void * unionfs_init(struct fuse_conn_info *conn) { // just to prevent the compiler complaining about unused variables (void) conn->max_readahead; // we only now (from unionfs_init) may go into the chroot, since otherwise // fuse_main() will fail to open /dev/fuse and to call mount if (uopt.chroot) { int res = chroot(uopt.chroot); if (res) { USYSLOG(LOG_WARNING, "Chdir to %s failed: %s ! Aborting!\n", uopt.chroot, strerror(errno)); exit(1); } } return NULL; }
static int unionfs_ioctl(const char *path, int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, void *data) { (void) path; (void) arg; // avoid compiler warning (void) fi; // avoid compiler warning fprintf(stderr, "Got ioctl: %d\n", cmd); #ifdef FUSE_IOCTL_COMPAT // 32bit-mode within 64-bit if (flags & FUSE_IOCTL_COMPAT) return -ENOSYS; #endif switch (cmd) { case UNIONFS_ONOFF_DEBUG: { int on_off = *((int *) data); // unionfs-ctl gives the opposite value, so !! bool setRes = set_debug_onoff(!!on_off); if (!setRes) return -EINVAL; return 0; } case UNIONFS_SET_DEBUG_FILE: { char *debug_path = (char *) data; set_debug_path(debug_path, _IOC_SIZE(cmd)); debug_init(); return 0; } default: USYSLOG(LOG_ERR, "Unknown ioctl: %d", cmd); return -EINVAL; break; } return 0; }
/** * Actually create the directory here. */ static int do_create(const char *path, int nbranch_ro, int nbranch_rw) { DBG("%s\n", path); char dirp[PATHLEN_MAX]; // dir path to create sprintf(dirp, "%s%s", uopt.branches[nbranch_rw].path, path); struct stat buf; int res = stat(dirp, &buf); if (res != -1) RETURN(0); // already exists if (nbranch_ro == nbranch_rw) { // special case nbranch_ro = nbranch_rw, this is if we a create // unionfs meta directories, so not directly on cow operations buf.st_mode = S_IRWXU | S_IRWXG; } else { // data from the ro-branch char o_dirp[PATHLEN_MAX]; // the pathname we want to copy sprintf(o_dirp, "%s%s", uopt.branches[nbranch_ro].path, path); res = stat(o_dirp, &buf); if (res == -1) RETURN(1); // lower level branch removed in the mean time? } res = mkdir(dirp, buf.st_mode); if (res == -1) { USYSLOG(LOG_DAEMON, "Creating %s failed: \n", dirp); RETURN(1); } if (nbranch_ro == nbranch_rw) RETURN(0); // the special case again if (setfile(dirp, &buf)) RETURN(1); // directory already removed by another process? // TODO: time, but its values are modified by the next dir/file creation steps? RETURN(0); }
/** * unionfs rename function * TODO: If we rename a directory on a read-only branch, we need to copy over * all files to the renamed directory on the read-write branch. */ static int unionfs_rename(const char *from, const char *to) { DBG("from %s to %s\n", from, to); bool is_dir = false; // is 'from' a file or directory int j = find_rw_branch_cutlast(to); if (j == -1) RETURN(-errno); int i = find_rorw_branch(from); if (i == -1) RETURN(-errno); if (!uopt.branches[i].rw) { i = find_rw_branch_cow_common(from, true); if (i == -1) RETURN(-errno); } if (i != j) { USYSLOG(LOG_ERR, "%s: from and to are on different writable branches %d vs %d, which" "is not supported yet.\n", __func__, i, j); RETURN(-EXDEV); } char f[PATHLEN_MAX], t[PATHLEN_MAX]; if (BUILD_PATH(f, uopt.branches[i].path, from)) RETURN(-ENAMETOOLONG); if (BUILD_PATH(t, uopt.branches[i].path, to)) RETURN(-ENAMETOOLONG); filetype_t ftype = path_is_dir(f); if (ftype == NOT_EXISTING) RETURN(-ENOENT); else if (ftype == IS_DIR) is_dir = true; int res; if (!uopt.branches[i].rw) { // since original file is on a read-only branch, we copied the from file to a writable branch, // but since we will rename from, we also need to hide the from file on the read-only branch if (is_dir) res = hide_dir(from, i); else res = hide_file(from, i); if (res) RETURN(-errno); } res = rename(f, t); if (res == -1) { int err = errno; // unlink() might overwrite errno // if from was on a read-only branch we copied it, but now rename failed so we need to delete it if (!uopt.branches[i].rw) { if (unlink(f)) USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now " "also unlink() failed\n", __func__, from); if (remove_hidden(from, i)) USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now " "also removing the whiteout failed\n", __func__, from); } RETURN(-err); } if (uopt.branches[i].rw) { // A lower branch still *might* have a file called 'from', we need to delete this. // We only need to do this if we have been on a rw-branch, since we created // a whiteout for read-only branches anyway. if (is_dir) maybe_whiteout(from, i, WHITEOUT_DIR); else maybe_whiteout(from, i, WHITEOUT_FILE); } remove_hidden(to, i); // remove hide file (if any) RETURN(0); }
int couchdb_update(uint64_t key, char *data, size_t len) { char *db_key = NULL; lcb_size_t key_size; lcb_error_t ret; int lock_result = -1; lcb_store_cmd_t item; const lcb_store_cmd_t *const store_items[] = { &item }; char *compressed_data = NULL; size_t compressed_len = 0; int res = 0; compressed_len = snappy_max_compressed_length(len); compressed_data = (char *)malloc(compressed_len); if (compressed_data == NULL) { syslog(LOG_ERR, "%s: Unable to allocate memory for compressed_data\n", __FUNCTION__); return (-ENOMEM); } if ((res = snappy_compress((const char *)data, len, compressed_data, &compressed_len)) != SNAPPY_OK) { free(compressed_data); return (-res); } memset(&item, 0, sizeof(lcb_store_cmd_t)); db_key = (char *) malloc(MAX_KEY_SIZE); if (NULL == db_key) { syslog(LOG_ERR, "%s: Unable to allocate memory for db_key\n", __FUNCTION__); return -ENOMEM; } sprintf(db_key,"%"PRId64"", key); USYSLOG(LOG_INFO, "%s: set1 %"PRId64" %s %s, \n", __FUNCTION__, key, (char *) data, db_key); key_size = strlen(db_key); item.v.v0.key = db_key; item.v.v0.nkey = key_size; item.v.v0.bytes = compressed_data; item.v.v0.nbytes = compressed_len; item.v.v0.operation = LCB_REPLACE; lock_result = pthread_rwlock_wrlock(&db_rwlock); if (lock_result != 0) { syslog(LOG_ERR, "%s: Failed to obtain write lock. Error = %d\n", __FUNCTION__, errno); return -errno; } ret = lcb_store(couchdb_handle, NULL, 1, store_items); if (ret != LCB_SUCCESS) { syslog(LOG_ERR, "%s: lcb_store failed with error %s\n", __FUNCTION__, lcb_strerror(couchdb_handle, ret)); return -ret; } /* Block the thread till this operation completes */ lcb_wait(couchdb_handle); pthread_rwlock_unlock(&db_rwlock); return (1); }