// link static int mhdd_link(const char *from, const char *to) { mhdd_debug(MHDD_MSG, "mhdd_link: from = %s to = %s\n", from, to); int dir_id = find_path_id(from); if (dir_id == -1) { errno = ENOENT; return -errno; } int res = create_parent_dirs(dir_id, to); if (res != 0) { return res; } char *path_from = create_path(mhdd.dirs[dir_id], from); char *path_to = create_path(mhdd.dirs[dir_id], to); res = link(path_from, path_to); free(path_from); free(path_to); if (res == 0) return 0; return -errno; }
gboolean rb_uri_create_parent_dirs (const char *uri, GError **error) { GFile *file; GFile *parent; gboolean ret; #if !GLIB_CHECK_VERSION(2,17,1) GError *l_error = NULL; #endif file = g_file_new_for_uri (uri); parent = g_file_get_parent (file); g_object_unref (file); if (parent == NULL) { /* now what? */ return TRUE; } #if GLIB_CHECK_VERSION(2,17,1) ret = check_file_is_directory (parent, error); if (ret == FALSE && *error == NULL) { ret = g_file_make_directory_with_parents (parent, NULL, error); } #else ret = create_parent_dirs (parent, &l_error); if (l_error != NULL) { g_propagate_error (error, l_error); } #endif g_object_unref (parent); return ret; }
// create or open static int mhdd_internal_open(const char *file, mode_t mode, struct fuse_file_info *fi, int what) { mhdd_debug(MHDD_INFO, "mhdd_internal_open: %s, flags = 0x%X\n", file, fi->flags); int dir_id, fd; char *path = find_path(file); if (path) { if (what == CREATE_FUNCTION) fd = open(path, fi->flags, mode); else fd = open(path, fi->flags); if (fd == -1) { free(path); return -errno; } struct flist *add = flist_create(file, path, fi->flags, fd); fi->fh = add->id; flist_unlock(); free(path); return 0; } mhdd_debug(MHDD_INFO, "mhdd_internal_open: new file %s\n", file); if ((dir_id = get_free_dir()) < 0) { errno = ENOSPC; return -errno; } create_parent_dirs(dir_id, file); path = create_path(mhdd.dirs[dir_id], file); if (what == CREATE_FUNCTION) fd = open(path, fi->flags, mode); else fd = open(path, fi->flags); if (fd == -1) { free(path); return -errno; } if (getuid() == 0) { struct stat st; gid_t gid = fuse_get_context()->gid; if (fstat(fd, &st) == 0) { /* parent directory is SGID'ed */ if (st.st_gid != getgid()) gid = st.st_gid; } fchown(fd, fuse_get_context()->uid, gid); } struct flist *add = flist_create(file, path, fi->flags, fd); fi->fh = add->id; flist_unlock(); free(path); return 0; }
/* write out a stats file */ void stats_write(const char *path, struct counters *counters) { size_t i; char *tmp_file; FILE *f; tmp_file = format("%s.tmp.%s", path, tmp_string()); f = fopen(tmp_file, "wb"); if (!f && errno == ENOENT) { if (create_parent_dirs(path) == 0) { f = fopen(tmp_file, "wb"); } } if (!f) { cc_log("Failed to open %s", tmp_file); goto end; } for (i = 0; i < counters->size; i++) { if (fprintf(f, "%u\n", counters->data[i]) < 0) { fatal("Failed to write to %s", tmp_file); } } fclose(f); x_rename(tmp_file, path); end: free(tmp_file); }
// mknod static int mhdd_mknod(const char *path, mode_t mode, dev_t rdev) { mhdd_debug(MHDD_MSG, "mhdd_mknod: path = %s mode = %X\n", path, mode); int res, i; char *nod; char *parent = get_parent_path(path); if (!parent) { errno = ENOENT; return -errno; } int dir_id = find_path_id(parent); free(parent); if (dir_id == -1) { errno = ENOENT; return -errno; } for (i = 0; i < 2; i++) { if (i) { if ((dir_id = get_free_dir())<0) { errno = ENOSPC; return -errno; } create_parent_dirs(dir_id, path); } nod = create_path(mhdd.dirs[dir_id], path); if (S_ISREG(mode)) { res = open(nod, O_CREAT | O_EXCL | O_WRONLY, mode); if (res >= 0) res = close(res); } else if (S_ISFIFO(mode)) res = mkfifo(nod, mode); else res = mknod(nod, mode, rdev); if (res != -1) { if (getuid() == 0) { struct fuse_context * fcontext = fuse_get_context(); chown(nod, fcontext->uid, fcontext->gid); } free(nod); return 0; } free(nod); if (errno != ENOSPC) return -errno; } return -errno; }
// mkdir static int mhdd_mkdir(const char * path, mode_t mode) { mhdd_debug(MHDD_MSG, "mhdd_mkdir: %s mode = %04X\n", path, mode); if (find_path_id(path) != -1) { errno = EEXIST; return -errno; } char *parent = get_parent_path(path); if (!parent) { errno = EFAULT; return -errno; } if (find_path_id(parent) == -1) { free(parent); errno = EFAULT; return -errno; } free(parent); int dir_id = get_free_dir(); if (dir_id<0) { errno = ENOSPC; return -errno; } create_parent_dirs(dir_id, path); char *name = create_path(mhdd.dirs[dir_id], path); if (mkdir(name, mode) == 0) { if (getuid() == 0) { struct stat st; gid_t gid = fuse_get_context()->gid; if (lstat(name, &st) == 0) { /* parent directory is SGID'ed */ if (st.st_gid != getgid()) gid = st.st_gid; } chown(name, fuse_get_context()->uid, gid); } free(name); return 0; } free(name); return -errno; }
static gboolean create_parent_dirs (GFile *file, GError **error) { gboolean ret; GFile *parent; ret = check_file_is_directory (file, error); if (ret == TRUE || *error != NULL) { return ret; } parent = g_file_get_parent (file); ret = create_parent_dirs (parent, error); g_object_unref (parent); if (ret == FALSE) { return FALSE; } return g_file_make_directory (file, NULL, error); }
// symlink static int mhdd_symlink(const char *from, const char *to) { mhdd_debug(MHDD_MSG, "mhdd_symlink: from = %s to = %s\n", from, to); int i, res; char *parent = get_parent_path(to); if (!parent) { errno = ENOENT; return -errno; } int dir_id = find_path_id(parent); free(parent); if (dir_id == -1) { errno = ENOENT; return -errno; } for (i = 0; i < 2; i++) { if (i) { if ((dir_id = get_free_dir()) < 0) { errno = ENOSPC; return -errno; } create_parent_dirs(dir_id, to); } char *path_to = create_path(mhdd.dirs[dir_id], to); res = symlink(from, path_to); free(path_to); if (res == 0) return 0; if (errno != ENOSPC) return -errno; } return -errno; }
int create_parent_dirs(int dir_id, const char *path) { mhdd_debug(MHDD_DEBUG, "create_parent_dirs: dir_id=%d, path=%s\n", dir_id, path); char *parent=get_parent_path(path); if (!parent) return 0; char *exists=find_path(parent); if (!exists) { free(parent); errno=EFAULT; return -errno; } char *path_parent=create_path(mhdd.dirs[dir_id], parent); struct stat st; // already exists if (stat(path_parent, &st)==0) { free(exists); free(path_parent); free(parent); return 0; } // create parent dirs int res=create_parent_dirs(dir_id, parent); if (res!=0) { free(path_parent); free(parent); free(exists); return res; } // get stat from exists dir if (stat(exists, &st)!=0) { free(exists); free(path_parent); free(parent); return -errno; } res=mkdir(path_parent, st.st_mode); if (res==0) { chown(path_parent, st.st_uid, st.st_gid); chmod(path_parent, st.st_mode); } else { res=-errno; mhdd_debug(MHDD_DEBUG, "create_parent_dirs: can not create dir %s: %s\n", path_parent, strerror(errno)); } #ifndef WITHOUT_XATTR // copy extended attributes of parent dir if (copy_xattrs(exists, path_parent) == -1) mhdd_debug(MHDD_MSG, "copy_xattrs: error copying xattrs from %s to %s\n", exists, path_parent); #endif free(exists); free(path_parent); free(parent); return res; }
int move_file(struct flist * file, off_t wsize) { char *from, *to, *buf; off_t size; FILE *input, *output; int ret, dir_id; struct utimbuf ftime = {0}; struct statvfs svf; fsblkcnt_t space; struct stat st; mhdd_debug(MHDD_MSG, "move_file: %s\n", file->real_name); /* TODO: it would be nice to contrive something alter */ flist_wrlock_locked(); from=file->real_name; /* We need to check if already moved */ if (statvfs(from, &svf) != 0) return -errno; space = svf.f_bsize; space *= svf.f_bavail; /* get file size */ if (fstat(file->fh, &st) != 0) { mhdd_debug(MHDD_MSG, "move_file: error stat %s: %s\n", from, strerror(errno)); return -errno; } /* Hard link support is limited to a single device, and files with >1 hardlinks cannot be moved between devices since this would (a) result in partial files on the source device (b) not free the space from the source device during unlink. */ if (st.st_nlink > 1) { mhdd_debug(MHDD_MSG, "move_file: cannot move " "files with >1 hardlinks\n"); return -ENOTSUP; } size = st.st_size; if (size < wsize) size=wsize; if (space > size) { mhdd_debug(MHDD_MSG, "move_file: we have enough space\n"); return 0; } if ((dir_id=find_free_space(size)) == -1) { mhdd_debug(MHDD_MSG, "move_file: can not find space\n"); return -1; } if (!(input = fopen(from, "r"))) return -errno; create_parent_dirs(dir_id, file->name); to = create_path(mhdd.dirs[dir_id], file->name); if (!(output = fopen(to, "w+"))) { ret = -errno; mhdd_debug(MHDD_MSG, "move_file: error create %s: %s\n", to, strerror(errno)); free(to); fclose(input); return(ret); } mhdd_debug(MHDD_MSG, "move_file: move %s to %s\n", from, to); // move data buf=(char *)calloc(sizeof(char), MOVE_BLOCK_SIZE); while((size = fread(buf, sizeof(char), MOVE_BLOCK_SIZE, input))) { if (size != fwrite(buf, sizeof(char), size, output)) { mhdd_debug(MHDD_MSG, "move_file: error move data to %s: %s\n", to, strerror(errno)); fclose(output); fclose(input); free(buf); unlink(to); free(to); return -1; } } free(buf); mhdd_debug(MHDD_MSG, "move_file: done move data\n"); fclose(input); // owner/group/permissions fchmod(fileno(output), st.st_mode); fchown(fileno(output), st.st_uid, st.st_gid); fclose(output); // time ftime.actime = st.st_atime; ftime.modtime = st.st_mtime; utime(to, &ftime); #ifndef WITHOUT_XATTR // extended attributes if (copy_xattrs(from, to) == -1) mhdd_debug(MHDD_MSG, "copy_xattrs: error copying xattrs from %s to %s\n", from, to); #endif from = strdup(from); if ((ret = reopen_files(file, to)) == 0) unlink(from); else unlink(to); mhdd_debug(MHDD_MSG, "move_file: %s -> %s: done, code=%d\n", from, to, ret); free(to); free(from); return ret; }
// rename static int mhdd_rename(const char *from, const char *to) { mhdd_debug(MHDD_MSG, "mhdd_rename: from = %s to = %s\n", from, to); int i, res; struct stat sto, sfrom; char *obj_from, *obj_to; int from_is_dir = 0, to_is_dir = 0, from_is_file = 0, to_is_file = 0; int to_dir_is_empty = 1, to_is_link = 0; if (strcmp(from, to) == 0) return 0; /* seek for possible errors */ for (i = 0; i < mhdd.cdirs; i++) { obj_to = create_path(mhdd.dirs[i], to); obj_from = create_path(mhdd.dirs[i], from); if (stat(obj_to, &sto) == 0) { if (S_ISDIR(sto.st_mode)) { to_is_dir++; if (!dir_is_empty(obj_to)) to_dir_is_empty = 0; } else to_is_file++; if (lstat(obj_to, &sto) == 0) { if ((sto.st_mode & S_IFMT) == S_IFLNK) to_is_link++; } } if (stat(obj_from, &sfrom) == 0) { if (S_ISDIR (sfrom.st_mode)) from_is_dir++; else from_is_file++; } free(obj_from); free(obj_to); if (to_is_file && from_is_dir) return -ENOTDIR; if (to_is_file && to_is_dir) return -ENOTEMPTY; if (from_is_dir && !to_dir_is_empty && !to_is_link) return -ENOTEMPTY; } /* parent 'to' path doesn't exists */ char *pto = get_parent_path (to); if (find_path_id(pto) == -1) { free (pto); return -ENOENT; } free (pto); /* rename cycle */ for (i = 0; i < mhdd.cdirs; i++) { obj_to = create_path(mhdd.dirs[i], to); obj_from = create_path(mhdd.dirs[i], from); if (lstat(obj_from, &sfrom) == 0) { /* if from is dir and at the same time file, we only rename dir, using lstat because we should handle symlinks here as well */ if (from_is_dir && from_is_file) { if (!S_ISDIR(sfrom.st_mode)) { free(obj_from); free(obj_to); continue; } } create_parent_dirs(i, to); mhdd_debug(MHDD_MSG, "mhdd_rename: rename %s -> %s\n", obj_from, obj_to); res = rename(obj_from, obj_to); if (res == -1) { free(obj_from); free(obj_to); return -errno; } } else { /* from and to are files, so we must remove to files */ if (from_is_file && to_is_file && !from_is_dir) { if (stat(obj_to, &sto) == 0) { mhdd_debug(MHDD_MSG, "mhdd_rename: unlink %s\n", obj_to); if (unlink(obj_to) == -1) { free(obj_from); free(obj_to); return -errno; } } } } free(obj_from); free(obj_to); } return 0; }
// rename static int mhdd_rename(const char *from, const char *to) { mhdd_debug(MHDD_MSG, "mhdd_rename: from = %s to = %s\n", from, to); int i, res; struct stat sto, sfrom; char *obj_from, *obj_to; int from_is_dir = 0, to_is_dir = 0, from_is_file = 0, to_is_file = 0; int to_dir_is_empty = 1; if (strcmp(from, to) == 0) return 0; /* seek for possible errors */ for (i = 0; i < mhdd.cdirs; i++) { obj_to = create_path(mhdd.dirs[i], to); obj_from = create_path(mhdd.dirs[i], from); if (stat(obj_to, &sto) == 0) { if (S_ISDIR(sto.st_mode)) { to_is_dir++; if (!dir_is_empty(obj_to)) to_dir_is_empty = 0; } else to_is_file++; } if (stat(obj_from, &sfrom) == 0) { if (S_ISDIR (sfrom.st_mode)) from_is_dir++; else from_is_file++; } free(obj_from); free(obj_to); if (to_is_file && from_is_dir) return -ENOTDIR; if (to_is_file && to_is_dir) return -ENOTEMPTY; if (from_is_dir && !to_dir_is_empty) return -ENOTEMPTY; } /* parent 'to' path doesn't exists */ char *pto = get_parent_path (to); if (find_path_id(pto) == -1) { free (pto); return -ENOENT; } free (pto); int *renamed_on = calloc(mhdd.cdirs, sizeof(int)); if (!renamed_on) return -ENOMEM; /* rename first, then unlink, so we never see a nonexistent file */ for (i = 0; i < mhdd.cdirs; i++) { obj_to = create_path(mhdd.dirs[i], to); obj_from = create_path(mhdd.dirs[i], from); if (stat(obj_from, &sfrom) == 0) { /* if from is dir and at the same time file, we only rename dir */ if (from_is_dir && from_is_file) { if (!S_ISDIR(sfrom.st_mode)) { free(obj_from); free(obj_to); continue; } } create_parent_dirs(i, to); mhdd_debug(MHDD_MSG, "mhdd_rename: rename %s -> %s\n", obj_from, obj_to); res = rename(obj_from, obj_to); if (res == -1) { free(obj_from); free(obj_to); free(renamed_on); return -errno; } renamed_on[i] = 1; } free(obj_from); free(obj_to); } /* now unlink */ for (i = 0; i < mhdd.cdirs; i++) { /* don't delete if we already renamed. */ if (renamed_on[i]) continue; obj_to = create_path(mhdd.dirs[i], to); obj_from = create_path(mhdd.dirs[i], from); if (stat(obj_from, &sfrom) != 0) { /* from and to are files, so we must remove to files */ if (from_is_file && to_is_file && !from_is_dir) { if (stat(obj_to, &sto) == 0) { mhdd_debug(MHDD_MSG, "mhdd_rename: unlink %s\n", obj_to); if (unlink(obj_to) == -1) { free(obj_from); free(obj_to); return -errno; } } } } free(obj_from); free(obj_to); } free(renamed_on); return 0; }
/* * This function acquires a lockfile for the given path. Returns true if the * lock was acquired, otherwise false. If the lock has been considered stale * for the number of microseconds specified by staleness_limit, the function * will (if possible) break the lock and then try to acquire it again. The * staleness limit should be reasonably larger than the longest time the lock * can be expected to be held, and the updates of the locked path should * probably be made with an atomic rename(2) to avoid corruption in the rare * case that the lock is broken by another process. */ bool lockfile_acquire(const char *path, unsigned staleness_limit) { char *lockfile = format("%s.lock", path); char *my_content = NULL, *content = NULL, *initial_content = NULL; const char *hostname = get_hostname(); bool acquired = false; #ifdef _WIN32 const size_t bufsize = 1024; int fd, len; #else int ret; #endif unsigned to_sleep = 1000, slept = 0; /* Microseconds. */ while (1) { free(my_content); my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(NULL)); #ifdef _WIN32 fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0666); if (fd == -1) { cc_log("lockfile_acquire: open WRONLY %s: %s", lockfile, strerror(errno)); if (errno == ENOENT) { /* Directory doesn't exist? */ if (create_parent_dirs(lockfile) == 0) { /* OK. Retry. */ continue; } } if (errno != EEXIST) { /* Directory doesn't exist or isn't writable? */ goto out; } /* Someone else has the lock. */ fd = open(lockfile, O_RDONLY|O_BINARY); if (fd == -1) { if (errno == ENOENT) { /* * The file was removed after the open() call above, so retry * acquiring it. */ continue; } else { cc_log("lockfile_acquire: open RDONLY %s: %s", lockfile, strerror(errno)); goto out; } } free(content); content = x_malloc(bufsize); if ((len = read(fd, content, bufsize - 1)) == -1) { cc_log("lockfile_acquire: read %s: %s", lockfile, strerror(errno)); close(fd); goto out; } close(fd); content[len] = '\0'; } else { /* We got the lock. */ if (write(fd, my_content, strlen(my_content)) == -1) { cc_log("lockfile_acquire: write %s: %s", lockfile, strerror(errno)); close(fd); x_unlink(lockfile); goto out; } close(fd); acquired = true; goto out; } #else ret = symlink(my_content, lockfile); if (ret == 0) { /* We got the lock. */ acquired = true; goto out; } cc_log("lockfile_acquire: symlink %s: %s", lockfile, strerror(errno)); if (errno == ENOENT) { /* Directory doesn't exist? */ if (create_parent_dirs(lockfile) == 0) { /* OK. Retry. */ continue; } } if (errno != EEXIST) { /* Directory doesn't exist or isn't writable? */ goto out; } free(content); content = x_readlink(lockfile); if (!content) { if (errno == ENOENT) { /* * The symlink was removed after the symlink() call above, so retry * acquiring it. */ continue; } else { cc_log("lockfile_acquire: readlink %s: %s", lockfile, strerror(errno)); goto out; } } #endif if (str_eq(content, my_content)) { /* Lost NFS reply? */ cc_log("lockfile_acquire: symlink %s failed but we got the lock anyway", lockfile); acquired = true; goto out; } /* * A possible improvement here would be to check if the process holding the * lock is still alive and break the lock early if it isn't. */ cc_log("lockfile_acquire: lock info for %s: %s", lockfile, content); if (!initial_content) { initial_content = x_strdup(content); } if (slept > staleness_limit) { if (str_eq(content, initial_content)) { /* The lock seems to be stale -- break it. */ cc_log("lockfile_acquire: breaking %s", lockfile); if (lockfile_acquire(lockfile, staleness_limit)) { lockfile_release(path); lockfile_release(lockfile); to_sleep = 1000; slept = 0; continue; } } cc_log("lockfile_acquire: gave up acquiring %s", lockfile); goto out; } cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds", lockfile, to_sleep); usleep(to_sleep); slept += to_sleep; to_sleep *= 2; } out: if (acquired) { cc_log("Acquired lock %s", lockfile); } else { cc_log("Failed to acquire lock %s", lockfile); } free(lockfile); free(my_content); free(initial_content); free(content); return acquired; }