// .utimens static int mhdd_utimens(const char *path, const struct timespec ts[2]) { mhdd_debug(MHDD_MSG, "mhdd_utimens: %s\n", path); int i, res, flag_found; for (i = flag_found = 0; i<mhdd.cdirs; i++) { char *object = create_path(mhdd.dirs[i], path); struct stat st; if (lstat(object, &st) != 0) { free(object); continue; } flag_found = 1; struct timeval tv[2]; tv[0].tv_sec = ts[0].tv_sec; tv[0].tv_usec = ts[0].tv_nsec / 1000; tv[1].tv_sec = ts[1].tv_sec; tv[1].tv_usec = ts[1].tv_nsec / 1000; res = lutimes(object, tv); free(object); if (res == -1) return -errno; } if (flag_found) return 0; errno = ENOENT; return -errno; }
// write static int mhdd_write(const char *path, const char *buf, size_t count, off_t offset, struct fuse_file_info *fi) { ssize_t res; struct flist *info; mhdd_debug(MHDD_INFO, "mhdd_write: %s, handle = %lld\n", path, fi->fh); info = flist_item_by_id(fi->fh); if (!info) { errno = EBADF; return -errno; } res = pwrite(info->fh, buf, count, offset); if ((res == count) || (res == -1 && errno != ENOSPC)) { flist_unlock(); if (res == -1) { mhdd_debug(MHDD_DEBUG, "mhdd_write: error write %s: %s\n", info->real_name, strerror(errno)); return -errno; } return res; } // end free space if (move_file(info, offset + count) == 0) { res = pwrite(info->fh, buf, count, offset); flist_unlock(); if (res == -1) { mhdd_debug(MHDD_DEBUG, "mhdd_write: error restart write: %s\n", strerror(errno)); return -errno; } if (res < count) { mhdd_debug(MHDD_DEBUG, "mhdd_write: error (re)write file %s %s\n", info->real_name, strerror(ENOSPC)); } return res; } errno = ENOSPC; flist_unlock(); return -errno; }
// get diridx for maximum free space int get_free_dir(void) { int i, max, max_perc, max_perc_space = 0; struct statvfs stf; fsblkcnt_t max_space = 0; for (max = i = 0; i < mhdd.cdirs; i++) { if (statvfs(mhdd.dirs[i], &stf) != 0) continue; fsblkcnt_t space = stf.f_bsize; space *= stf.f_bavail; if (mhdd.move_limit <= 100) { int perc; if (mhdd.move_limit != 100) { fsblkcnt_t perclimit = stf.f_blocks; if (mhdd.move_limit != 99) { perclimit *= mhdd.move_limit + 1; perclimit /= 100; } if (stf.f_bavail >= perclimit) return i; } perc = 100 * stf.f_bavail / stf.f_blocks; if (perc > max_perc_space) { max_perc_space = perc; max_perc = i; } } else { if (space >= mhdd.move_limit) return i; } if(space > max_space) { max_space = space; max = i; } } if (!max_space && !max_perc_space) { mhdd_debug(MHDD_INFO, "get_free_dir: Can't find freespace\n"); return -1; } if (max_perc_space) return max_perc; return max; }
// close static int mhdd_release(const char *path, struct fuse_file_info *fi) { struct flist *del; int fh; mhdd_debug(MHDD_MSG, "mhdd_release: %s, handle = %lld\n", path, fi->fh); del = flist_item_by_id_wrlock(fi->fh); if (!del) { mhdd_debug(MHDD_INFO, "mhdd_release: unknown file number: %llu\n", fi->fh); errno = EBADF; return -errno; } fh = del->fh; flist_delete_wrlocked(del); close(fh); return 0; }
// 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; }
// rmdir static int mhdd_rmdir(const char * path) { mhdd_debug(MHDD_MSG, "mhdd_rmdir: %s\n", path); char *dir; while((dir = find_path(path))) { int res = rmdir(dir); free(dir); if (res == -1) return -errno; } return 0; }
// unlink static int mhdd_unlink(const char *path) { mhdd_debug(MHDD_MSG, "mhdd_unlink: %s\n", path); char *file = find_path(path); if (!file) { errno = ENOENT; return -errno; } int res = unlink(file); free(file); if (res == -1) return -errno; return 0; }
// getattr static int mhdd_stat(const char *file_name, struct stat *buf) { mhdd_debug(MHDD_MSG, "mhdd_stat: %s\n", file_name); char *path = find_path(file_name); if (path) { int ret = lstat(path, buf); free(path); if (ret == -1) return -errno; return 0; } errno = ENOENT; return -errno; }
// readlink static int mhdd_readlink(const char *path, char *buf, size_t size) { mhdd_debug(MHDD_MSG, "mhdd_readlink: %s, size = %d\n", path, size); char *link = find_path(path); if (link) { memset(buf, 0, size); int res = readlink(link, buf, size); free(link); if (res >= 0) return 0; } return -1; }
// truncate static int mhdd_truncate(const char *path, off_t size) { char *file = find_path(path); mhdd_debug(MHDD_MSG, "mhdd_truncate: %s\n", path); if (file) { int res = truncate(file, size); free(file); if (res == -1) return -errno; return 0; } errno = ENOENT; return -errno; }
static int mhdd_getxattr(const char *path, const char *attrname, char *buf, size_t count) { int size = 0; char * real_path = find_path(path); if (!real_path) return -ENOENT; mhdd_debug(MHDD_MSG, "mhdd_getxattr: path = %s name = %s bufsize = %d\n", real_path, attrname, count); size = getxattr(real_path, attrname, buf, count); free(real_path); if (size == -1) return -errno; return size; }
static int mhdd_removexattr(const char *path, const char *attrname) { char * real_path = find_path(path); if (!real_path) return -ENOENT; mhdd_debug(MHDD_MSG, "mhdd_removexattr: path = %s name = %s\n", real_path, attrname); int res = removexattr(real_path, attrname); free(real_path); if (res == -1) return -errno; return 0; }
// access static int mhdd_access(const char *path, int mask) { mhdd_debug(MHDD_MSG, "mhdd_access: %s mode = %04X\n", path, mask); char *file = find_path(path); if (file) { int res = access(file, mask); free(file); if (res == -1) return -errno; return 0; } errno = ENOENT; return -errno; }
static int mhdd_setxattr(const char *path, const char *attrname, const char *attrval, size_t attrvalsize, int flags) { char * real_path = find_path(path); if (!real_path) return -ENOENT; mhdd_debug(MHDD_MSG, "mhdd_setxattr: path = %s name = %s value = %s size = %d\n", real_path, attrname, attrval, attrvalsize); int res = setxattr(real_path, attrname, attrval, attrvalsize, flags); free(real_path); if (res == -1) return -errno; return 0; }
static int mhdd_listxattr(const char *path, char *buf, size_t count) { int ret = 0; char * real_path = find_path(path); if (!real_path) return -ENOENT; mhdd_debug(MHDD_MSG, "mhdd_listxattr: path = %s bufsize = %d\n", real_path, count); ret=listxattr(real_path, buf, count); free(real_path); if (ret == -1) return -errno; return ret; }
// 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; }
// ftrucate static int mhdd_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) { int res; struct flist *info; mhdd_debug(MHDD_MSG, "mhdd_ftruncate: %s, handle = %lld\n", path, fi->fh); info = flist_item_by_id(fi->fh); if (!info) { errno = EBADF; return -errno; } int fh = info->fh; res = ftruncate(fh, size); flist_unlock(); if (res == -1) return -errno; return 0; }
// read static int mhdd_read(const char *path, char *buf, size_t count, off_t offset, struct fuse_file_info *fi) { ssize_t res; struct flist * info; mhdd_debug(MHDD_INFO, "mhdd_read: %s, offset = %lld, count = %lld\n", path, (long long)offset, (long long)count ); info = flist_item_by_id(fi->fh); if (!info) { errno = EBADF; return -errno; } res = pread(info->fh, buf, count, offset); flist_unlock(); if (res == -1) return -errno; return res; }
// 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; }
// .chmod static int mhdd_chmod(const char *path, mode_t mode) { mhdd_debug(MHDD_MSG, "mhdd_chmod: mode = 0x%03X %s\n", mode, path); int i, res, flag_found; for (i = flag_found = 0; i<mhdd.cdirs; i++) { char *object = create_path(mhdd.dirs[i], path); struct stat st; if (lstat(object, &st) != 0) { free(object); continue; } flag_found = 1; res = chmod(object, mode); free(object); if (res == -1) return -errno; } if (flag_found) return 0; errno = ENOENT; return -errno; }
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; }
int copy_xattrs(const char *from, const char *to) { int listsize=0, attrvalsize=0; char *listbuf=NULL, *attrvalbuf=NULL, *name_begin=NULL, *name_end=NULL; // if not xattrs on source, then do nothing if ((listsize=listxattr(from, NULL, 0)) == 0) return 0; // get all extended attributes listbuf=(char *)calloc(sizeof(char), listsize); if (listxattr(from, listbuf, listsize) == -1) { mhdd_debug(MHDD_MSG, "listxattr: error listing xattrs on %s : %s\n", from, strerror(errno)); return -1; } // loop through each xattr for(name_begin=listbuf, name_end=listbuf+1; name_end < (listbuf + listsize); name_end++) { // skip the loop if we're not at the end of an attribute name if (*name_end != '\0') continue; // get the size of the extended attribute attrvalsize = getxattr(from, name_begin, NULL, 0); if (attrvalsize < 0) { mhdd_debug(MHDD_MSG, "getxattr: error getting xattr size on %s name %s : %s\n", from, name_begin, strerror(errno)); return -1; } // get the value of the extended attribute attrvalbuf=(char *)calloc(sizeof(char), attrvalsize); if (getxattr(from, name_begin, attrvalbuf, attrvalsize) < 0) { mhdd_debug(MHDD_MSG, "getxattr: error getting xattr value on %s name %s : %s\n", from, name_begin, strerror(errno)); return -1; } // set the value of the extended attribute on dest file if (setxattr(to, name_begin, attrvalbuf, attrvalsize, 0) < 0) { mhdd_debug(MHDD_MSG, "setxattr: error setting xattr value on %s name %s : %s\n", from, name_begin, strerror(errno)); return -1; } free(attrvalbuf); // point the pointer to the start of the attr name to the start // of the next attr name_begin=name_end+1; name_end++; } free(listbuf); return 0; }
static int reopen_files(struct flist * file, const char *new_name) { int i; struct flist ** rlist; int error = 0; mhdd_debug(MHDD_INFO, "reopen_files: %s -> %s\n", file->real_name, new_name); rlist = flist_items_by_eq_name(file); if (!rlist) return 0; for (i = 0; rlist[i]; i++) { struct flist * next = rlist[i]; off_t seek = lseek(next->fh, 0, SEEK_CUR); int flags = next->flags; int fh; flags &= ~(O_EXCL|O_TRUNC); // open if ((fh = open(new_name, flags)) == -1) { mhdd_debug(MHDD_INFO, "reopen_files: error reopen: %s\n", strerror(errno)); if (!i) { error = errno; break; } close(next->fh); } else { // seek if (seek != lseek(fh, seek, SEEK_SET)) { mhdd_debug(MHDD_INFO, "reopen_files: error seek %s\n", strerror(errno)); close(fh); if (!i) { error = errno; break; } } // filehandle if (dup2(fh, next->fh) != next->fh) { mhdd_debug(MHDD_INFO, "reopen_files: error dup2 %s\n", strerror(errno)); close(fh); if (!i) { error = errno; break; } } // close temporary filehandle mhdd_debug(MHDD_MSG, "reopen_files: reopened %s (to %s) old h=%x " "new h=%x seek=%lld\n", next->real_name, new_name, next->fh, fh, seek); close(fh); } } if (error) { free(rlist); return -error; } /* change real_name */ for (i = 0; rlist[i]; i++) { free(rlist[i]->real_name); rlist[i]->real_name = strdup(new_name); } free(rlist); return 0; }
static int mhdd_readdir( const char *dirname, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info * fi) { int i, j, found; mhdd_debug(MHDD_MSG, "mhdd_readdir: %s\n", dirname); char **dirs = (char **) calloc(mhdd.cdirs+1, sizeof(char *)); struct stat st; typedef struct dir_item { char *name; struct stat *st; } dir_item; // find all dirs for(i = j = found = 0; i<mhdd.cdirs; i++) { char *path = create_path(mhdd.dirs[i], dirname); if (stat(path, &st) == 0) { found++; if (S_ISDIR(st.st_mode)) { dirs[j] = path; j++; continue; } } free(path); } // dirs not found if (dirs[0] == 0) { errno = ENOENT; if (found) errno = ENOTDIR; free(dirs); return -errno; } GHashTable* hash = g_hash_table_new(g_str_hash, g_str_equal); // read directories for (i = 0; dirs[i]; i++) { struct dirent *de; DIR * dh = opendir(dirs[i]); if (!dh) continue; while((de = readdir(dh))) { // find dups if(g_hash_table_lookup(hash, de->d_name)) { continue; } // add item char *object_name = create_path(dirs[i], de->d_name); struct dir_item *new_item = calloc(1, sizeof(struct dir_item)); new_item->name = strdup(de->d_name); new_item->st = calloc(1, sizeof(struct stat)); lstat(object_name, new_item->st); g_hash_table_insert(hash, new_item->name, new_item); free(object_name); } closedir(dh); } dir_item *item; gpointer key, value; GHashTableIter iter; g_hash_table_iter_init(&iter, hash); while(g_hash_table_iter_next (&iter, &key, &value)) { item = (dir_item*) value; int result = filler(buf, item->name, item->st, 0); free(item->name); free(item->st); free(item); if(result) break; } g_hash_table_destroy(hash); for (i = 0; dirs[i]; i++) free(dirs[i]); free(dirs); 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, 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; }
//statvfs static int mhdd_statfs(const char *path, struct statvfs *buf) { int i, j; struct statvfs * stats; struct stat st; dev_t * devices; mhdd_debug(MHDD_MSG, "mhdd_statfs: %s\n", path); stats = calloc(mhdd.cdirs, sizeof(struct statvfs)); devices = calloc(mhdd.cdirs, sizeof(dev_t)); for (i = 0; i < mhdd.cdirs; i++) { int ret = statvfs(mhdd.dirs[i], stats+i); if (ret != 0) { free(stats); free(devices); return -errno; } ret = stat(mhdd.dirs[i], &st); if (ret != 0) { free(stats); free(devices); return -errno; } devices[i] = st.st_dev; } unsigned long min_block = stats[0].f_bsize, min_frame = stats[0].f_frsize; for (i = 1; i<mhdd.cdirs; i++) { if (min_block>stats[i].f_bsize) min_block = stats[i].f_bsize; if (min_frame>stats[i].f_frsize) min_frame = stats[i].f_frsize; } if (!min_block) min_block = 512; if (!min_frame) min_frame = 512; for (i = 0; i < mhdd.cdirs; i++) { if (stats[i].f_bsize>min_block) { stats[i].f_bfree *= stats[i].f_bsize/min_block; stats[i].f_bavail *= stats[i].f_bsize/min_block; stats[i].f_bsize = min_block; } if (stats[i].f_frsize>min_frame) { stats[i].f_blocks *= stats[i].f_frsize/min_frame; stats[i].f_frsize = min_frame; } } memcpy(buf, stats, sizeof(struct statvfs)); for (i = 1; i<mhdd.cdirs; i++) { /* if the device already processed, skip it */ if (devices[i]) { int dup_found = 0; for (j = 0; j < i; j++) { if (devices[j] == devices[i]) { dup_found = 1; break; } } if (dup_found) continue; } if (buf->f_namemax<stats[i].f_namemax) { buf->f_namemax = stats[i].f_namemax; } buf->f_ffree += stats[i].f_ffree; buf->f_files += stats[i].f_files; buf->f_favail += stats[i].f_favail; buf->f_bavail += stats[i].f_bavail; buf->f_bfree += stats[i].f_bfree; buf->f_blocks += stats[i].f_blocks; } free(stats); free(devices); return 0; }
static int mhdd_readdir( const char *dirname, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info * fi) { int i, j, found; mhdd_debug(MHDD_MSG, "mhdd_readdir: %s\n", dirname); char **dirs = (char **) calloc(mhdd.cdirs+1, sizeof(char *)); typedef struct dir_item { char *name; struct stat *st; UT_hash_handle hh; } dir_item; dir_item * items_ht = NULL; struct stat st; // find all dirs for(i = j = found = 0; i<mhdd.cdirs; i++) { char *path = create_path(mhdd.dirs[i], dirname); if (stat(path, &st) == 0) { found++; if (S_ISDIR(st.st_mode)) { dirs[j] = path; j++; continue; } } free(path); } // dirs not found if (dirs[0] == 0) { errno = ENOENT; if (found) errno = ENOTDIR; free(dirs); return -errno; } // read directories for (i = 0; dirs[i]; i++) { struct dirent *de; DIR * dh = opendir(dirs[i]); if (!dh) continue; while((de = readdir(dh))) { // find dups struct dir_item *prev; HASH_FIND_STR(items_ht, de->d_name, prev); if (prev) { continue; } // add item char *object_name = create_path(dirs[i], de->d_name); struct dir_item *new_item = calloc(1, sizeof(struct dir_item)); new_item->name = strdup(de->d_name); new_item->st = calloc(1, sizeof(struct stat)); lstat(object_name, new_item->st); HASH_ADD_KEYPTR( hh, items_ht, new_item->name, strlen(new_item->name), new_item ); free(object_name); } closedir(dh); } dir_item *item, *tmp; // fill list HASH_ITER(hh, items_ht, item, tmp) { if (filler(buf, item->name, item->st, 0)) break; } // free memory HASH_ITER(hh, items_ht, item, tmp) { free(item->name); free(item->st); free(item); }
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; }
// 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; }