> While I was thinking about Wilhelm's question, I've got an idea of new
> option "dirs=<branch>=ro_wh".
> Currently these options are 'ro' and 'rw'. A 'ro_wh' option is
> perfectly equivalent to current 'ro'. But the meaning of 'ro' option
> changes. In case of 'ro', unionfs does not lookup a whiteout entry, but
> 'ro_wh' does.

I have tried, here is a patch.
This is for simple lookup and unionctl, not including unionfs inode
operations (unionfs_create, etc.).


Junjiro Okajima


diff -rup ./unionfs-20051130-1554.orig/branchman.c 
./unionfs-20051130-1554/branchman.c
--- ./unionfs-20051130-1554.orig/branchman.c    2005-12-01 05:54:06.000000000 
+0900
+++ ./unionfs-20051130-1554/branchman.c 2005-12-16 16:29:50.000000000 +0900
@@ -211,7 +211,7 @@ int unionfs_ioctl_addbranch(struct inode
                goto out;
 
        err = -EINVAL;
-       if (addargs->ab_perms & ~(MAY_READ | MAY_WRITE))
+       if (addargs->ab_perms & ~(MAY_READ | MAY_WRITE | MAY_WHITEOUT))
                goto out;
        if (!(addargs->ab_perms & MAY_READ))
                goto out;
@@ -514,7 +514,7 @@ int unionfs_ioctl_rdwrbranch(struct inod
        if (rdwrargs->rwb_branch < 0
            || (rdwrargs->rwb_branch > (sbend(inode->i_sb) + 1)))
                goto out;
-       if (rdwrargs->rwb_perms & ~(MAY_READ | MAY_WRITE))
+       if (rdwrargs->rwb_perms & ~(MAY_READ | MAY_WRITE | MAY_WHITEOUT))
                goto out;
        if (!(rdwrargs->rwb_perms & MAY_READ))
                goto out;
diff -rup ./unionfs-20051130-1554.orig/lookup.c ./unionfs-20051130-1554/lookup.c
--- ./unionfs-20051130-1554.orig/lookup.c       2005-12-01 05:54:06.000000000 
+0900
+++ ./unionfs-20051130-1554/lookup.c    2005-12-16 16:29:50.000000000 +0900
@@ -41,7 +41,8 @@ struct dentry *unionfs_lookup_backend(st
        int opaque;
        char *whname = NULL;
        const char *name;
-       int namelen;
+       int namelen, whlen;
+       struct super_block *sb;
 
        print_entry("mode = %d", lookupmode);
        PASSERT(dentry);
@@ -82,6 +83,12 @@ struct dentry *unionfs_lookup_backend(st
                err = -EPERM;
                goto out_free;
        }
+       whname = get_whname(name, namelen, &whlen);
+       if (IS_ERR(whname)) {
+               err = PTR_ERR(whname);
+               whname = NULL;
+               goto out_free;
+       }
 
        /* must initialize dentry operations */
        dentry->d_op = &unionfs_dops;
@@ -91,6 +98,7 @@ struct dentry *unionfs_lookup_backend(st
        bend = dbend(parent_dentry);
        bopaque = dbopaque(parent_dentry);
        ASSERT(bstart >= 0);
+       sb = dentry->d_sb;
 
        /* It would be ideal if we could convert partial lookups to only have
         * to do this work when they really need to.  It could probably improve
@@ -119,44 +127,37 @@ struct dentry *unionfs_lookup_backend(st
                if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode))
                        continue;
 
-               /* Reuse the whiteout name because its value doesn't change. */
-               if (!whname) {
-                       whname = KMALLOC(namelen + 5, GFP_UNIONFS);
-                       if (!whname) {
-                               err = -ENOMEM;
+               if (branchperms(sb, bindex) & (MAY_WRITE | MAY_WHITEOUT)) {
+                       /* check if whiteout exists in this branch: lookup 
.wh.foo */
+                       wh_hidden_dentry = LOOKUP_ONE_LEN(whname, 
hidden_dir_dentry,
+                                                         whlen);
+                       if (IS_ERR(wh_hidden_dentry)) {
+                               err = PTR_ERR(wh_hidden_dentry);
                                goto out_free;
                        }
-                       strcpy(whname, WHPFX);
-                       strncat(whname, name, namelen);
-                       whname[WHLEN + namelen] = '\0';
-               }
 
-               /* check if whiteout exists in this branch: lookup .wh.foo */
-               wh_hidden_dentry = LOOKUP_ONE_LEN(whname, hidden_dir_dentry,
-                                                 namelen + WHLEN);
-               if (IS_ERR(wh_hidden_dentry)) {
-                       err = PTR_ERR(wh_hidden_dentry);
-                       goto out_free;
-               }
+                       if (wh_hidden_dentry->d_inode) {
+                               DPUT(wh_hidden_dentry);
+                               /* We found a whiteout so lets give up. */
+                               fist_dprint(8, "whiteout found in %d\n", 
bindex);
+                               if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) 
{
+                                       set_dbend(dentry, bindex);
+                                       set_dbopaque(dentry, bindex);
+                                       break;
+                               }
+                               err = -EIO;
+                               printk(KERN_NOTICE "EIO: Invalid whiteout entry 
type"
+                                      " %d.\n", 
wh_hidden_dentry->d_inode->i_mode);
+                               goto out_free;
+                       }
 
-               if (wh_hidden_dentry->d_inode) {
                        DPUT(wh_hidden_dentry);
-                       /* We found a whiteout so lets give up. */
-                       fist_dprint(8, "whiteout found in %d\n", bindex);
-                       if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) {
-                               set_dbend(dentry, bindex);
-                               set_dbopaque(dentry, bindex);
-                               break;
-                       }
-                       err = -EIO;
-                       printk(KERN_NOTICE "EIO: Invalid whiteout entry type"
-                              " %d.\n", wh_hidden_dentry->d_inode->i_mode);
-                       goto out_free;
+                       wh_hidden_dentry = NULL;
+               } else {
+                       fist_dprint(8, "skipped bindex %d(0x%x)\n",
+                                   bindex, branchperms(sb, bindex));
                }
 
-               DPUT(wh_hidden_dentry);
-               wh_hidden_dentry = NULL;
-
                /* Now do regular lookup; lookup foo */
                hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry,
                                               namelen);
@@ -307,7 +308,7 @@ struct dentry *unionfs_lookup_backend(st
                ASSERT(dbend(dentry) <= sbmax(dentry->d_sb));
                ASSERT(dbstart(dentry) >= 0);
        }
-       KFREE(whname);
+       put_whname(whname);
        fist_print_dentry("OUT unionfs_lookup (parent)", parent_dentry);
        fist_print_dentry("OUT unionfs_lookup (child)", dentry);
        if (locked_parent)
diff -rup ./unionfs-20051130-1554.orig/main.c ./unionfs-20051130-1554/main.c
--- ./unionfs-20051130-1554.orig/main.c 2005-12-01 05:54:06.000000000 +0900
+++ ./unionfs-20051130-1554/main.c      2005-12-16 16:29:50.000000000 +0900
@@ -323,28 +323,31 @@ static int parse_dirs_option(struct supe
        branches = 0;
        while ((name = strsep(&options, ":")) != NULL) {
                int perms;
-               int l;
+               char *p;
 
                if (!*name)
                        continue;
 
                branches++;
 
-               /* strip off =rw or =ro if it is specified. */
-               l = strlen(name);
-               if (!strcmp(name + l - 3, "=ro")) {
-                       perms = MAY_READ;
-                       name[l - 3] = '\0';
-               } else if (!strcmp(name + l - 3, "=rw")) {
-                       perms = MAY_READ | MAY_WRITE;
-                       name[l - 3] = '\0';
-               } else {
-                       perms = MAY_READ | MAY_WRITE;
+               /* strip off =rw|ro|roh/ro_wh if it is specified. */
+               perms = MAY_READ | MAY_WRITE; /* default */
+               p = strchr(name, '=');
+               if (p) {
+                       *p++ = '\0';
+                       if (!strcmp(p, "ro"))
+                               perms = MAY_READ;
+                       else if (!strcmp(p, "roh") || !strcmp(p, "ro_wh"))
+                               perms = MAY_READ | MAY_WHITEOUT;
+                       else if (strcmp(p, "rw"))
+                               printk(KERN_WARNING
+                                      "unionfs: unknown option '%s'\n", p);
                }
 
-               fist_dprint(4, "unionfs: using directory: %s (%c%c)\n",
+               fist_dprint(4, "unionfs: using directory: %s (%c%c%c)\n",
                            name, perms & MAY_READ ? 'r' : '-',
-                           perms & MAY_WRITE ? 'w' : '-');
+                           perms & MAY_WRITE ? 'w' : '-',
+                           perms & MAY_WHITEOUT ? 'h' : '-');
 
                err = path_lookup(name, LOOKUP_FOLLOW, &nd);
                RECORD_PATH_LOOKUP(&nd);
diff -rup ./unionfs-20051130-1554.orig/rename.c ./unionfs-20051130-1554/rename.c
--- ./unionfs-20051130-1554.orig/rename.c       2005-12-01 05:54:06.000000000 
+0900
+++ ./unionfs-20051130-1554/rename.c    2005-12-16 16:29:50.000000000 +0900
@@ -668,42 +668,6 @@ static int unionfs_rename_all(struct ino
        return err;
 }
 
-/*
- * generate whiteout name, which is NOT terminated by NULL.
- * @name: original d_name.name
- * @len: original d_name.len
- * @whlen: length of whname
- * return value is the whname.
- * correctly returned value as whname must be freed later.
- * If an error occurs, returns PTR_ERR.
- */
-static inline char *get_whname(const char *name, int len, int *whlen)
-{
-       char *ret;
-
-       ASSERT(name && len && whlen);
-#if 0
-       /* TODO: check the full path length with PATH_MAX */
-       if (len > PAGE_SIZE - WHLEN + 1)
-               return ERR_PTR(-ENAMETOOLONG);
-#endif
-       ret = __getname();
-       if (!ret)
-               return ERR_PTR(-ENOMEM);
-
-       memcpy(ret, WHPFX, WHLEN);
-       memcpy(ret + WHLEN, name, len);
-       *whlen = len + WHLEN;
-       ret[*whlen] = '\0';
-
-       return ret;
-}
-
-static inline void put_whname(char *whname)
-{
-       __putname(whname);
-}
-
 static struct dentry *lookup_whiteout(struct dentry *dentry)
 {
        char *whname;
diff -rup ./unionfs-20051130-1554.orig/super.c ./unionfs-20051130-1554/super.c
--- ./unionfs-20051130-1554.orig/super.c        2005-12-01 05:54:06.000000000 
+0900
+++ ./unionfs-20051130-1554/super.c     2005-12-16 16:29:50.000000000 +0900
@@ -452,7 +452,8 @@ static int unionfs_show_options(struct s
                           PAGE_SIZE);
                perms = branchperms(sb, bindex);
                seq_printf(m, "%s=%s", hidden_path,
-                          perms & MAY_WRITE ? "rw" : "ro");
+                          perms & MAY_WRITE ? "rw" :
+                          perms & MAY_WHITEOUT ? "roh" : "ro");
                if (bindex != bend) {
                        seq_printf(m, ":");
                }
diff -rup ./unionfs-20051130-1554.orig/unionctl.c 
./unionfs-20051130-1554/unionctl.c
--- ./unionfs-20051130-1554.orig/unionctl.c     2005-12-01 05:54:06.000000000 
+0900
+++ ./unionfs-20051130-1554/unionctl.c  2005-12-16 16:29:50.000000000 +0900
@@ -35,6 +35,7 @@
 
 #define MAY_READ 4
 #define MAY_WRITE 2
+#define MAY_WHITEOUT 32
 
 extern int find_union(const char *path, char **options, char **actual_path,
                      int uniononly);
@@ -59,9 +60,9 @@ void __attribute__ ((__noreturn__)) __us
        fprintf(stderr,
                "ACTION can be --add, --remove, --mode, --list, or 
--query.\nFurther arguments depend on ACTION.\n");
        fprintf(stderr,
-               "\tunionctl UNION --add [ --before BRANCH | --after BRANCH ] [ 
--mode (rw|ro) ] DIRECTORY\n");
+               "\tunionctl UNION --add [ --before BRANCH | --after BRANCH ] [ 
--mode (rw|ro|roh) ] DIRECTORY\n");
        fprintf(stderr, "\tunionctl UNION --remove BRANCH\n");
-       fprintf(stderr, "\tunionctl UNION --mode BRANCH (rw|ro)\n");
+       fprintf(stderr, "\tunionctl UNION --mode BRANCH (rw|ro|roh)\n");
        fprintf(stderr, "\tunionctl UNION --list\n");
        fprintf(stderr, "\tunionctl FILENAME --query\n");
        fprintf(stderr,
@@ -69,6 +70,18 @@ void __attribute__ ((__noreturn__)) __us
        exit(EXIT_FAILURE);
 }
 
+static inline int parse_rw(char *p)
+{
+       if (strcmp(p, "ro") == 0)
+               return MAY_READ;
+       else if (strcmp(p, "roh") == 0 || strcmp(p, "ro_wh") == 0)
+               return MAY_READ | MAY_WHITEOUT;
+       else if (strcmp(p, "rw") == 0)
+               return MAY_READ | MAY_WRITE;
+       else
+               return 0;
+}
+
 static char **parse_options(char *options)
 {
        char **ret = NULL;
@@ -77,8 +90,7 @@ static char **parse_options(char *option
        char *p;
        char *q;
        char *r;
-       char *s;
-       int l;
+       char *s, *t, *u;
 
        p = options;
        do {
@@ -107,15 +119,18 @@ static char **parse_options(char *option
                                        exit(EXIT_FAILURE);
                                }
 
-                               l = strlen(r);
-                               if (((r[l - 1] == 'o') || (r[l - 1] == 'w'))
-                                   && (r[l - 2] == 'r') && (r[l - 3] == '=')) {
-                                       r[l - 3] = '\0';
-                                       branchperms[n - 1] = MAY_READ;
-                                       if (r[l - 1] == 'w') {
-                                               branchperms[n - 1] |= MAY_WRITE;
-                                       }
+                               t = strchr(r, '=');
+                               u = t+1;
+                               if (!t || !u || !*u)
+                                       goto err;
+                               *t = 0;
+                               branchperms[n - 1] = parse_rw(u);
+                               if (!branchperms[n - 1]) {
+                               err:
+                                       fprintf(stderr, "cannot parse '%s'\n", 
r);
+                                       exit(EXIT_FAILURE);
                                }
+
                                ret[n - 1] = strdup(r);
                                ret[n] = NULL;
 
@@ -162,10 +177,11 @@ static void dump_branches(const char *pr
        int n = 0;
 
        while (branches[n]) {
-               char r, w;
+               char r, w, h;
                r = (branchperms[n] & MAY_READ) ? 'r' : '-';
                w = (branchperms[n] & MAY_WRITE) ? 'w' : '-';
-               printf("%s%s (%c%c)\n", prefix, branches[n], r, w);
+               h = (branchperms[n] & MAY_WHITEOUT) ? 'h' : '-';
+               printf("%s%s (%c%c%c)\n", prefix, branches[n], r, w, h);
                n++;
        }
 }
@@ -312,14 +328,10 @@ int main(int argc, char *argv[])
                                                usage();
                                        }
 
-                                       if (!strcmp(argv[i], "ro")) {
-                                               addargs.ab_perms = MAY_READ;
-                                       } else if (!strcmp(argv[i], "rw")) {
-                                               addargs.ab_perms =
-                                                   MAY_READ | MAY_WRITE;
-                                       } else {
+                                       addargs.ab_perms = parse_rw(argv[i]);
+                                       if (!addargs.ab_perms) {
                                                fprintf(stderr,
-                                                       "Valid modes are ro and 
rw you specified: \"%s\"\n",
+                                                       "Valid modes are 
ro/rw/roh you specified: \"%s\"\n",
                                                        argv[i]);
                                                usage();
                                        }
@@ -379,20 +391,15 @@ int main(int argc, char *argv[])
                        usage();
                }
 
-               if (!strcmp(argv[4], "ro")) {
-                       rdwrargs.rwb_perms = MAY_READ;
-                       branchnum = 3;
-               } else if (!strcmp(argv[4], "rw")) {
-                       rdwrargs.rwb_perms = MAY_READ | MAY_WRITE;
-                       branchnum = 3;
-               } else if (!strcmp(argv[3], "ro")) {
-                       rdwrargs.rwb_perms = MAY_READ;
-                       branchnum = 4;
-               } else if (!strcmp(argv[3], "rw")) {
-                       rdwrargs.rwb_perms = MAY_READ | MAY_WRITE;
+               branchnum = 3;
+               rdwrargs.rwb_perms = parse_rw(argv[4]);
+               if (!rdwrargs.rwb_perms) {
                        branchnum = 4;
-               } else {
-                       usage();
+                       rdwrargs.rwb_perms = parse_rw(argv[3]);
+                       if (!rdwrargs.rwb_perms) {
+                               usage();
+                               exit(EXIT_FAILURE);
+                       }
                }
 
                if (realpath(argv[branchnum], resolv_bp) == NULL) {
@@ -478,13 +485,14 @@ int main(int argc, char *argv[])
                }
 
                for (i = 0; i <= ret; i++) {
-                       char r, w;
+                       char r, w, h;
                        r = (branchperms[i] & MAY_READ) ? 'r' : '-';
                        w = (branchperms[i] & MAY_WRITE) ? 'w' : '-';
+                       h = (branchperms[i] & MAY_WHITEOUT) ? 'h' : '-';
 
                        if (FD_ISSET(i, &branchlist))
-                               printf("%s\t%s (%c%c)\n", argv[unionpos],
-                                      branches[i], r, w);
+                               printf("%s\t%s (%c%c%c)\n", argv[unionpos],
+                                      branches[i], r, w, h);
                }
                break;
        }
diff -rup ./unionfs-20051130-1554.orig/unionfs.h 
./unionfs-20051130-1554/unionfs.h
--- ./unionfs-20051130-1554.orig/unionfs.h      2005-12-01 05:54:06.000000000 
+0900
+++ ./unionfs-20051130-1554/unionfs.h   2005-12-16 16:29:50.000000000 +0900
@@ -118,6 +118,20 @@ struct putmap {
 };
 
 /* unionfs super-block data in memory */
+/*
+ * flags for branchperm.
+ * MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND in linux/fs.h.
+ * MAY_GENERIC is for unionfs_permission() and try linux
+ * generic_permission() or something after the branch i_op->permission()
+ * returned an error.
+ * MAY_GENERIC is set when the branch is marked as "<branch>=rog".
+ * MAY_WHITEOUT is for unionfs_lookup() and try lookup whiteout on readonly
+ * branch.
+ * MAY_HITEOUT is set when the branch is marked as "<branch>=roh".
+ */
+#define __UNIONFS_PERM_LAST    MAY_APPEND
+//#define MAY_GENERIC          (__UNIONFS_PERM_LAST<<1)
+#define MAY_WHITEOUT           (__UNIONFS_PERM_LAST<<2)
 struct unionfs_sb_info {
        int b_end;
 
@@ -640,7 +654,42 @@ static inline int __is_robranch(struct d
 #define UNIONFS_DIR_OPAQUE_NAME "__dir_opaque"
 #define UNIONFS_DIR_OPAQUE WHPFX UNIONFS_DIR_OPAQUE_NAME
 
-;
+/*
+ * generate whiteout name, which is NOT terminated by NULL.
+ * @name: original d_name.name
+ * @len: original d_name.len
+ * @whlen: length of whname
+ * return value is the whname.
+ * correctly returned value as whname must be freed later.
+ * If an error occurs, returns PTR_ERR.
+ */
+static inline char *get_whname(const char *name, int len, int *whlen)
+{
+       char *ret;
+
+       ASSERT(name && len && whlen);
+#if 0
+       /* TODO: check the full path length with PATH_MAX */
+       if (len > PAGE_SIZE - WHLEN + 1)
+               return ERR_PTR(-ENAMETOOLONG);
+#endif
+       ret = __getname();
+       if (!ret)
+               return ERR_PTR(-ENOMEM);
+
+       memcpy(ret, WHPFX, WHLEN);
+       memcpy(ret + WHLEN, name, len);
+       *whlen = len + WHLEN;
+       ret[*whlen] = '\0';
+
+       return ret;
+}
+
+static inline void put_whname(char *whname)
+{
+       __putname(whname);
+}
+
 /*
 * Defines,structs,and functions for persistant used by kenrel and user
 */
_______________________________________________
unionfs mailing list
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs

Reply via email to