> 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