From eff68fdc7031f546102818fa7498ae5e5af77a64 Mon Sep 17 00:00:00 2001 From: Nekun Date: Thu, 24 Feb 2022 08:56:59 +0000 Subject: [PATCH] Initial implementation of --delete-filtered option --- src/bindfs.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/src/bindfs.c b/src/bindfs.c index c5dce96..9bcb0b9 100644 --- a/src/bindfs.c +++ b/src/bindfs.c @@ -65,6 +65,7 @@ #endif #include // For dirname(), basename() +#include // For nftw() in --delete-filtered impl #ifdef __linux__ #include @@ -134,6 +135,7 @@ static struct Settings { RateLimiter *write_limiter; FileFilter *filefilter; + int delete_filtered; enum CreatePolicy { CREATE_AS_USER, @@ -600,6 +602,14 @@ static int chown_new_file(const char *path, struct fuse_context *fc, int (*chown return 0; } +/* nftw() callback */ +int delete_recurse(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + int (*delfunc)(const char*) = S_ISDIR(sb->st_mode) ? rmdir : unlink; + + return delfunc(fpath) ? errno : 0; +} + static int delete_file(const char *path, int (*target_delete_func)(const char *)) { int res; char *real_path; @@ -618,6 +628,65 @@ static int delete_file(const char *path, int (*target_delete_func)(const char *) if (filefilter_check(real_path,0,NULL) == -1) return -errno; + /* Remove hidden with --file-filter files before rmdir(), to + * avoid ENOTEMPTY */ + if (settings.delete_filtered && main_delete_func == rmdir) { + DIR *dp = NULL; + struct dirent *de = malloc(sizeof(struct dirent)); + int e_cnt = 0, nf_cnt = 0; + char *e_path = strdup(real_path); + int e_path_len = strlen(e_path); + + if (lstat(real_path,&st) == -1) + goto delfil_out; + if (!S_ISDIR(st.st_mode)) + goto delfil_out; + + if ((dp = opendir(real_path))) { + while ((de = readdir(dp))) { + if (strcmp(de->d_name,".") == 0 || strcmp(de->d_name,"..") == 0) + continue; + e_cnt++; + if (filefilter_find_match(settings.filefilter,de->d_name,de->d_type << 12) == filefilter_status_notfound) + nf_cnt++; + } + + /* empty directory or non-filtered entries exists */ + if(!e_cnt || nf_cnt) + goto delfil_out; + + rewinddir(dp); + while ((de = readdir(dp))) { + /* should contain only filtered entries here, don't recheck */ + if (strcmp(de->d_name,".") == 0 || strcmp(de->d_name,"..") == 0) + continue; + + e_path = realloc(e_path,e_path_len+strlen(de->d_name)+2); + if (e_path[e_path_len-1] != '/') + e_path[e_path_len] = '/'; + strcpy(e_path+e_path_len+1,de->d_name); + if (de->d_type == DT_DIR) { + /* delete it recursively, don't follow symlinks */ + int ret = nftw(e_path,delete_recurse,8,FTW_DEPTH|FTW_MOUNT|FTW_PHYS); + if (ret < 0) + return -ENOTEMPTY; + else if (ret > 0) + return -ret; + } else { + if (unlink(e_path) != 0) + return -errno; + } + } + } else { + return -errno; + } +delfil_out: + if (dp) + closedir(dp); + free(de); + free(e_path); + } + if (settings.resolve_symlinks) { if (lstat(real_path, &st) == -1) { free(real_path); @@ -1835,7 +1904,8 @@ static void print_usage(const char *progname) " --create-with-perms=... Alter permissions of new files.\n" "\n" "File filtering policy:\n" - " --file-filter=*.png/... Hide files in underlying dir.\n" + " --file-filter=*.png/... Hide files in target filesystem.\n" + " --delete-filtered Remove hidden files in removing directory\n" "\n" "Chown policy:\n" " --chown-normal Try to chown the original files (the default).\n" @@ -1902,6 +1972,7 @@ enum OptionKey { OPTKEY_FUSE_VERSION, OPTKEY_CREATE_AS_USER, OPTKEY_CREATE_AS_MOUNTER, + OPTKEY_DELETE_FILTERED, OPTKEY_CHOWN_NORMAL, OPTKEY_CHOWN_IGNORE, OPTKEY_CHOWN_DENY, @@ -1991,6 +2062,10 @@ static int process_option(void *data, const char *arg, int key, settings.chmod_allow_x = 1; return 0; + case OPTKEY_DELETE_FILTERED: + settings.delete_filtered = 1; + return 0; + case OPTKEY_XATTR_NONE: settings.xattr_policy = XATTR_UNIMPLEMENTED; return 0; @@ -2617,6 +2692,8 @@ int main(int argc, char *argv[]) OPT2("--chgrp-ignore", "chgrp-ignore", OPTKEY_CHGRP_IGNORE), OPT2("--chgrp-deny", "chgrp-deny", OPTKEY_CHGRP_DENY), + OPT2("--delete-filtered", "delete-filtered", OPTKEY_DELETE_FILTERED), + OPT2("--chmod-normal", "chmod-normal", OPTKEY_CHMOD_NORMAL), OPT2("--chmod-ignore", "chmod-ignore", OPTKEY_CHMOD_IGNORE), OPT2("--chmod-deny", "chmod-deny", OPTKEY_CHMOD_DENY), @@ -2659,6 +2736,7 @@ int main(int argc, char *argv[]) settings.usermap = usermap_create(); settings.usermap_reverse = usermap_create(); settings.filefilter = filefilter_create(); + settings.delete_filtered = 0; settings.read_limiter = NULL; settings.write_limiter = NULL; settings.new_uid = -1; @@ -2833,11 +2911,16 @@ int main(int argc, char *argv[]) } } + /* Parse file filtering policy */ if (od.file_filter) { if (!parse_file_filter(settings.filefilter,od.file_filter)) { /* parse_file_filter() returned an error */ return 1; - }; + } + } + if (settings.delete_filtered && !od.file_filter) { + fprintf(stderr,"Error: --delete-filter must be used only with --file-filter specified\n"); + return 1; } if (od.forward_odirect) {