[adding fstests in CC with full patch inline to collect wisdom from other fs developers] On Fri, Nov 17, 2017 at 7:49 AM, zhangyi (F) <yi.zhang@xxxxxxxxxx> wrote: > Hi, > > Here is the origin version of fsck.overlay utility I mentioned last > week. It scan each underlying directory and check orphan whiteout, > invalid opaque xattr and invalid redirect xattr. We can use it to > check filesystem inconsistency before mount overlayfs to avoid some > unexpected results. Thanks for working on this! It looks like a great start. > > This patch can am to an empty git repository. I have already do basic > test list in 'test' directory. Please do a review give some suggestions. > Thanks a lot. Please consider, instead of writing overlay tests in new repo, to write the tests for xfstests and hook overlay.fsck to _check_test_fs/ _check_scratch_fs. See for example very basic index sanity checks I implemented here: https://github.com/amir73il/xfstests/commit/dac4da479e3e85be8f7beec54f384c0a832f74bd It may be useful, if Eryu agrees, to merge the "incubator" version of fsck.overlayfs into xfstests as a helper program until it starts getting packaged and distributed. Please consider this option. > > Thanks, > Yi. > > --------------------------- > > fsck.overlay > ============ > > fsck.overlay is used to check and optionally repair underlying > directories of overlay-filesystem. > > Check the following points: > > Whiteouts > --------- > > A whiteout is a character device with 0/0 device number. It is used to > record the removed files or directories, When a whiteout is found in a > directory, there should be at least one directory or file with the same > name in any of the corresponding lower layers. If not exist, the whiteout > will be treated as orphan whiteout and remove. > > Opaque directories > ------------------ > > An opaque directory is a directory with "trusted.overlay.opaque" xattr > valued "y". There are two legal situations of making opaque directory: 1) > create directory over whiteout 2) creat directory in merged directory. If an > opaque directory is found, corresponding matching name in lower layers might > exist or parent directory might merged, If not, the opaque xattr will be > treated as invalid and remove. > > Redirect directories > -------------------- > > An redirect directory is a directory with "trusted.overlay.redirect" > xattr valued to the path of the original location from the root of the > overlay. It is only used when renaming a directory and "redirect dir" > feature is enabled. If an redirect directory is found, the following > must be met: > > 1) The directory store in redirect xattr should exist in one of lower > layers. > 2) The origin directory should be redirected only once in one layer, > which mean there is only one redirect xattr point to this origin directory in > the specified layer. > 3) A whiteout or an opaque directory with the same name to origin should > exist in the same directory as the redirect directory. > > If not, 1) The redirect xattr is invalid and need to remove 2) One of > the redirect xattr is redundant but not sure which one is, ask user 3) > Create a whiteout device or set opaque xattr to an existing directory if the > parent directory was meregd or remove xattr if not. > > Usage > ===== > > 1. Ensure overlay filesystem is not mounted based on directories which > need to check. > > 2. Run fsck.overlay program: > Usage: > fsck.overlay [-l lowerdir] [-u upperdir] [-w workdir] [-avhV] > > Options: > -l, --lowerdir=LOWERDIR specify lower directories of overlayfs, > multiple lower use ':' as separator. > -u, --upperdir=UPPERDIR specify upper directory of overlayfs > -w, --workdir=WORKDIR specify work directory of overlayfs > -a, --auto repair automatically (no questions) Let stick with e2fsck conventions when possible. -a should be -y and we should definitely have -n, because this is how xfstest would run it. > -v, --verbose print more messages of overlayfs > -h, --help display this usage of overlayfs > -V, --version display version information > > 3. Exit value: > 0 No errors > 1 Filesystem errors corrected > 2 System should be rebooted > 4 Filesystem errors left uncorrected > 8 Operational error > 16 Usage or syntax error > 32 Checking canceled by user request > 128 Shared-library error > > Todo > ==== > > 1. Overlay filesystem mounted check. Prevent fscking when overlay is > online. Now, We cannot distinguish mounted directories if overlayfs was > mounted with relative path. This should be handled by kernel. We now already grab an advisory exclusive I_OVL_INUSE lock on both upperdir and workdir. fsck.overlay can try to open upperdir/workdir with O_EXCL|O_DIRECTORY and kernel should fail this open if overlayfs is holding the I_OVL_INUSE. Read the man page section on O_EXCL and block device. This is how e2fsck and friends get exclusive access w.r.t mount. > 2. Symbolic link check. > 3. Check origin/impure/nlink xattr. > 4. ... > > Signed-off-by: zhangyi (F) <yi.zhang@xxxxxxxxxx> > --- > Makefile | 31 +++ > README.md | 88 +++++++++ > check.c | 482 ++++++++++++++++++++++++++++++++++++++++++++++ > check.h | 7 + > common.c | 95 +++++++++ > common.h | 34 ++++ > config.h | 22 +++ > fsck.c | 179 +++++++++++++++++ > lib.c | 197 +++++++++++++++++++ > lib.h | 73 +++++++ > mount.c | 319 ++++++++++++++++++++++++++++++ > mount.h | 8 + > test/README.md | 12 ++ > test/auto_test.sh | 46 +++++ > test/clean.sh | 6 + > test/local.config | 16 ++ > test/src/opaque_test.sh | 144 ++++++++++++++ > test/src/prepare.sh | 15 ++ > test/src/redirect_test.sh | 163 ++++++++++++++++ > test/src/whiteout_test.sh | 63 ++++++ > 20 files changed, 2000 insertions(+) > create mode 100644 Makefile > create mode 100644 README.md > create mode 100644 check.c > create mode 100644 check.h > create mode 100644 common.c > create mode 100644 common.h > create mode 100644 config.h > create mode 100644 fsck.c > create mode 100644 lib.c > create mode 100644 lib.h > create mode 100644 mount.c > create mode 100644 mount.h > create mode 100644 test/README.md > create mode 100755 test/auto_test.sh > create mode 100755 test/clean.sh > create mode 100644 test/local.config > create mode 100755 test/src/opaque_test.sh > create mode 100755 test/src/prepare.sh > create mode 100755 test/src/redirect_test.sh > create mode 100755 test/src/whiteout_test.sh > > diff --git a/Makefile b/Makefile > new file mode 100644 > index 0000000..ced5005 > --- /dev/null > +++ b/Makefile > @@ -0,0 +1,31 @@ > +CFLAGS = -Wall -g > +LFLAGS = -lm > +CC = gcc > + > +all: overlay > + > +overlay: fsck.o common.o lib.o check.o mount.o > + $(CC) $(LFLAGS) fsck.o common.o lib.o check.o mount.o -o fsck.overlay > + > +fsck.o: > + $(CC) $(CFLAGS) -c fsck.c > + > +common.o: > + $(CC) $(CFLAGS) -c common.c > + > +lib.o: > + $(CC) $(CFLAGS) -c lib.c > + > +check.o: > + $(CC) $(CFLAGS) -c check.c > + > +mount.o: > + $(CC) $(CFLAGS) -c mount.c > + > +clean: > + rm -f *.o fsck.overlay > + rm -rf bin > + > +install: all > + mkdir bin > + cp fsck.overlay bin > diff --git a/README.md b/README.md > new file mode 100644 > index 0000000..8de69cd > --- /dev/null > +++ b/README.md > @@ -0,0 +1,88 @@ > +fsck.overlay > +============ > + > +fsck.overlay is used to check and optionally repair underlying directories > +of overlay-filesystem. > + > +Check the following points: > + > +Whiteouts > +--------- > + > +A whiteout is a character device with 0/0 device number. It is used to record > +the removed files or directories, When a whiteout is found in a directory, > +there should be at least one directory or file with the same name in any of the > +corresponding lower layers. If not exist, the whiteout will be treated as orphan > +whiteout and remove. > + > + > +Opaque directories > +------------------ > + > +An opaque directory is a directory with "trusted.overlay.opaque" xattr valued > +"y". There are two legal situations of making opaque directory: 1) create > +directory over whiteout 2) creat directory in merged directory. If an opaque > +directory is found, corresponding matching name in lower layers might exist or > +parent directory might merged, If not, the opaque xattr will be treated as > +invalid and remove. > + > + > +Redirect directories > +-------------------- > + > +An redirect directory is a directory with "trusted.overlay.redirect" xattr > +valued to the path of the original location from the root of the overlay. It > +is only used when renaming a directory and "redirect dir" feature is enabled. > +If an redirect directory is found, the following must be met: > + > +1) The directory store in redirect xattr should exist in one of lower layers. > +2) The origin directory should be redirected only once in one layer, which mean > + there is only one redirect xattr point to this origin directory in the > + specified layer. > +3) A whiteout or an opaque directory with the same name to origin should exist > + in the same directory as the redirect directory. another redirected upper dir can also be covering the redirect lower target > + > +If not, 1) The redirect xattr is invalid and need to remove 2) One of the > +redirect xattr is redundant but not sure which one is, ask user 3) Create a > +whiteout device or set opaque xattr to an existing directory if the parent > +directory was meregd or remove xattr if not. You can consult origin xattr, decode it and fix redirect accordingly. I have plans to implement this in kernel as well with 'verify_dir' mount option, but maybe is it better to do this with fsck tool. Restoring unset origin xattr, which I also sent a kernel patch for can also be done by fsck as Miklos also suggested. > + > +Usage > +===== > + > +1. Ensure overlay filesystem is not mounted based on directories which need to > + check. > + > +2. Run fsck.overlay program: > + Usage: > + fsck.overlay [-l lowerdir] [-u upperdir] [-w workdir] [-avhV] > + > + Options: > + -l, --lowerdir=LOWERDIR specify lower directories of overlayfs, > + multiple lower use ':' as separator. > + -u, --upperdir=UPPERDIR specify upper directory of overlayfs > + -w, --workdir=WORKDIR specify work directory of overlayfs > + -a, --auto repair automatically (no questions) > + -v, --verbose print more messages of overlayfs > + -h, --help display this usage of overlayfs > + -V, --version display version information > + > +3. Exit value: > + 0 No errors > + 1 Filesystem errors corrected > + 2 System should be rebooted > + 4 Filesystem errors left uncorrected > + 8 Operational error > + 16 Usage or syntax error > + 32 Checking canceled by user request > + 128 Shared-library error > + > +Todo > +==== > + > +1. Overlay filesystem mounted check. Prevent fscking when overlay is > + online. Now, We cannot distinguish mounted directories if overlayfs was > + mounted with relative path. > +2. Symbolic link check. > +3. Check origin/impure/nlink xattr. > +4. ... > diff --git a/check.c b/check.c > new file mode 100644 > index 0000000..1794501 > --- /dev/null > +++ b/check.c > @@ -0,0 +1,482 @@ > +/* > + * > + * Check and fix inconsistency for all underlying layers of overlay > + * > + * zhangyi (F) <yi.zhang@xxxxxxxxxx> - Sponsored by Huawei CR I guess you are not obliged to, but it would probably be better for you to assign copyright either to you or to your employer. And choosing a license explicitly is also recommended. IMO it would be best if you could keep at least part of the code under LGPL (or a like) and start with a mini liboverlay that can grow over time and be used by other projects. > + * > + */ > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <string.h> > +#include <errno.h> > +#include <unistd.h> > +#include <stdbool.h> > +#include <sys/types.h> > +#include <sys/xattr.h> > +#include <sys/stat.h> > +#include <linux/limits.h> > + > +#include "common.h" > +#include "lib.h" > +#include "check.h" > + > +/* Underlying information */ > +struct ovl_lower_check { > + unsigned int type; /* check extent type */ > + > + bool exist; > + char path[PATH_MAX]; /* exist pathname found, only valid if exist */ > + struct stat st; /* only valid if exist */ > +}; > + > +/* Redirect information */ > +struct ovl_redirect_entry { > + struct ovl_redirect_entry *next; > + > + char origin[PATH_MAX]; /* origin directory path */ > + > + char path[PATH_MAX]; /* redirect directory */ > + int dirtype; /* OVL_UPPER or OVL_LOWER */ > + int dirnum; /* only valid when dirtype==OVL_LOWER */ > +}; > + > +/* Whiteout */ > +#define WHITEOUT_DEV 0 > +#define WHITEOUT_MOD 0 > + > +extern char **lowerdir; > +extern char upperdir[]; > +extern char workdir[]; > +extern unsigned int lower_num; > +extern int flags; > +extern int status; > + > +static inline mode_t file_type(const struct stat *status) > +{ > + return status->st_mode & S_IFMT; > +} > + > +static inline bool is_whiteout(const struct stat *status) > +{ > + return (file_type(status) == S_IFCHR) && (status->st_rdev == WHITEOUT_DEV); > +} > + > +static inline bool is_dir(const struct stat *status) > +{ > + return file_type(status) == S_IFDIR; > +} > + > +static bool is_dir_xattr(const char *pathname, const char *xattrname) > +{ > + char val; > + ssize_t ret; > + > + ret = getxattr(pathname, xattrname, &val, 1); > + if ((ret < 0) && !(errno == ENODATA || errno == ENOTSUP)) { > + print_err(_("Cannot getxattr %s %s: %s\n"), pathname, > + xattrname, strerror(errno)); > + return false; > + } > + > + return (ret == 1 && val == 'y') ? true : false; > +} > + > +static inline bool ovl_is_opaque(const char *pathname) > +{ > + return is_dir_xattr(pathname, OVL_OPAQUE_XATTR); > +} > + > +static inline int ovl_remove_opaque(const char *pathname) > +{ > + return remove_xattr(pathname, OVL_OPAQUE_XATTR); > +} > + > +static inline int ovl_set_opaque(const char *pathname) > +{ > + return set_xattr(pathname, OVL_OPAQUE_XATTR, "y", 1); > +} > + > +static int ovl_get_redirect(const char *pathname, size_t dirlen, > + size_t filelen, char **redirect) > +{ > + char *buf = NULL; > + ssize_t ret; > + > + ret = get_xattr(pathname, OVL_REDIRECT_XATTR, &buf, NULL); > + if (ret <= 0 || !buf) > + return ret; > + > + if (buf[0] != '/') { > + size_t baselen = strlen(pathname)-filelen-dirlen; > + > + buf = srealloc(buf, ret + baselen + 1); > + memmove(buf + baselen, buf, ret); > + memcpy(buf, pathname+dirlen, baselen); > + buf[ret + baselen] = '\0'; > + } > + > + *redirect = buf; > + return 0; > +} > + > +static inline int ovl_remove_redirect(const char *pathname) > +{ > + return remove_xattr(pathname, OVL_REDIRECT_XATTR); > +} > + > +static inline int ovl_create_whiteout(const char *pathname) > +{ > + int ret; > + > + ret = mknod(pathname, S_IFCHR | WHITEOUT_MOD, makedev(0, 0)); > + if (ret) > + print_err(_("Cannot mknod %s:%s\n"), > + pathname, strerror(errno)); > + return ret; > +} > + > +/* > + * Scan each lower dir lower than 'start' and check type matching, > + * we stop scan if we found something. > + * > + * skip: skip whiteout. > + * > + */ > +static int ovl_check_lower(const char *path, unsigned int start, > + struct ovl_lower_check *chk, bool skip) > +{ > + char lower_path[PATH_MAX]; > + struct stat st; > + unsigned int i; > + > + for (i = start; i < lower_num; i++) { > + snprintf(lower_path, sizeof(lower_path), "%s%s", lowerdir[i], path); > + > + if (lstat(lower_path, &st) != 0) { IMO it would be better to keep open fd for lower layers root and use fstatat() with layer root fd. > + if (errno != ENOENT && errno != ENOTDIR) { > + print_err(_("Cannot stat %s: %s\n"), > + lower_path, strerror(errno)); > + return -1; > + } > + continue; > + } > + > + if (skip && is_whiteout(&st)) > + continue; > + > + chk->exist = true; > + chk->st = st; > + strncpy(chk->path, lower_path, sizeof(chk->path)); > + break; > + } > + > + return 0; > +} > + > +/* > + * Scan each underlying dirs under specified dir if a whiteout is > + * found, check it's orphan or not. In auto-mode, orphan whiteouts > + * will be removed directly. > + */ > +static int ovl_check_whiteout(struct scan_ctx *sctx) > +{ > + const char *pathname = sctx->pathname; > + const struct stat *st = sctx->st; > + struct ovl_lower_check chk = {0}; > + unsigned int start; > + int ret = 0; > + > + /* Is a whiteout ? */ > + if (!is_whiteout(st)) > + return 0; > + > + /* Is Whiteout in the bottom lower dir ? */ > + if (sctx->dirtype == OVL_LOWER && sctx->num == lower_num-1) > + goto remove; > + > + /* > + * Scan each corresponding lower directroy under this layer, > + * check is there a file or dir with the same name. > + */ > + start = (sctx->dirtype == OVL_LOWER) ? sctx->num + 1 : 0; > + ret = ovl_check_lower(pathname + sctx->dirlen, start, &chk, true); > + if (ret) > + return ret; > + if (chk.exist && !is_whiteout(&chk.st)) > + goto out; > + > +remove: > + /* Remove orphan whiteout directly or ask user */ > + print_info(_("Orphan whiteout: %s "), pathname); > + if (!ask_question("Remove", 1)) > + return 0; > + > + ret = unlink(pathname); > + if (ret) { > + print_err(_("Cannot unlink %s: %s\n"), pathname, > + strerror(errno)); > + return ret; > + } > +out: > + return ret; > +} > + > +/* > + * Scan each underlying under specified dir if an opaque dir is found, > + * check the opaque xattr is invalid or not. In auto-mode, invalid > + * opaque xattr will be removed directly. > + * Do the follow checking: > + * 1) Check lower matching name exist or not. > + * 2) Check parent dir is merged or pure. > + * If no lower matching and parent is not merged, remove opaque xattr. > + */ > +static int ovl_check_opaque(struct scan_ctx *sctx) > +{ > + const char *pathname = sctx->pathname; > + char parent_path[PATH_MAX]; > + struct ovl_lower_check chk = {0}; > + unsigned int start; > + int ret = 0; > + > + /* Is opaque ? */ > + if (!ovl_is_opaque(pathname)) > + goto out; > + > + /* Opaque dir in last lower dir ? */ > + if (sctx->dirtype == OVL_LOWER && sctx->num == lower_num-1) > + goto remove; > + > + /* > + * Scan each corresponding lower directroy under this layer, > + * check if there is a file or dir with the same name. > + */ > + start = (sctx->dirtype == OVL_LOWER) ? sctx->num + 1 : 0; > + ret = ovl_check_lower(pathname + sctx->dirlen, start, &chk, true); > + if (ret) > + return ret; > + if (chk.exist && !is_whiteout(&chk.st)) > + goto out; > + > + /* Check parent directory merged or pure */ > + memcpy(parent_path, pathname, sctx->pathlen-sctx->filelen); > + ret = ovl_check_lower(parent_path + sctx->dirlen, start, &chk, false); > + if (ret) > + return ret; > + if (chk.exist && is_dir(&chk.st)) > + goto out; > + > +remove: > + /* Remove opaque xattr or ask user */ > + print_info(_("Invalid opaque xattr: %s "), pathname); > + if (!ask_question("Remove", 1)) > + return 0; > + > + ret = ovl_remove_opaque(pathname); > +out: > + return ret; > +} > + > +static struct ovl_redirect_entry *redirect_list = NULL; > + > +static void ovl_redirect_entry_add(const char *path, int dirtype, > + int dirnum, const char *origin) > +{ > + struct ovl_redirect_entry *last, *new; > + > + new = smalloc(sizeof(*new)); > + > + print_debug(_("Redirect entry add: [%s %s %s %d]\n"), > + origin, path, (dirtype == OVL_UPPER) ? "UP" : "LOW", > + (dirtype == OVL_UPPER) ? 0 : dirnum); > + > + if (!redirect_list) { > + redirect_list = new; > + } else { > + for (last = redirect_list; last->next; last = last->next); > + last->next = new; > + } > + new->next = NULL; > + new->dirtype = dirtype; > + new->dirnum = dirnum; > + strncpy(new->origin, origin, sizeof(new->origin)); > + strncpy(new->path, path, sizeof(new->path)); > +} > + > +static bool vol_redirect_entry_find(const char *origin, int dirtype, > + int dirnum, char **founded) > +{ > + struct ovl_redirect_entry *entry; > + > + if (!redirect_list) > + return false; > + > + for (entry = redirect_list; entry; entry = entry->next) { > + bool same_layer; > + > + print_debug(_("Redirect entry found:[%s %s %s %d]\n"), > + entry->origin, entry->path, > + (entry->dirtype == OVL_UPPER) ? "UP" : "LOW", > + (entry->dirtype == OVL_UPPER) ? 0 : entry->dirnum); > + > + same_layer = ((entry->dirtype == dirtype) && > + (dirtype == OVL_LOWER ? (entry->dirnum == dirnum) : true)); > + > + if (same_layer && !strcmp(entry->origin, origin)) { > + *founded = entry->path; > + return true; > + } > + } > + > + return false; > +} > + > +static void vol_redirect_free(void) > +{ > + struct ovl_redirect_entry *entry; > + > + while (redirect_list) { > + entry = redirect_list; > + redirect_list = redirect_list->next; > + free(entry); > + } > +} > + > +/* > + * Get redirect origin directory stored in the xattr, check it's invlaid > + * or not, In auto-mode, invalid redirect xattr will be removed directly. > + * Do the follow checking: > + * 1) Check the origin directory exist or not. If not, remove xattr. > + * 2) Count how many directories the origin directory was redirected by. > + * If more than one in the same layer, there must be some inconsistency > + * but not sure, just warn. > + * 3) Check and fix the missing whiteout or opaque in redierct parent dir. > + */ > +static int ovl_check_redirect(struct scan_ctx *sctx) > +{ > + const char *pathname = sctx->pathname; > + struct ovl_lower_check chk = {0}; > + char redirect_rpath[PATH_MAX]; > + struct stat rst; > + char *redirect = NULL; > + unsigned int start; > + int ret = 0; > + > + /* Get redirect */ > + ret = ovl_get_redirect(pathname, sctx->dirlen, > + sctx->filelen, &redirect); > + if (ret || !redirect) > + return ret; > + > + print_debug(_("Dir %s has redirect %s\n"), pathname, redirect); > + > + /* Redirect dir in last lower dir ? */ > + if (sctx->dirtype == OVL_LOWER && sctx->num == lower_num-1) > + goto remove; > + > + /* Scan lower directories to check redirect dir exist or not */ > + start = (sctx->dirtype == OVL_LOWER) ? sctx->num + 1 : 0; > + ret = ovl_check_lower(redirect, start, &chk, false); > + if (ret) > + goto out; > + if (chk.exist && is_dir(&chk.st)) { > + char *tmp; > + > + /* Check duplicate in same layer */ > + if (vol_redirect_entry_find(chk.path, sctx->dirtype, > + sctx->num, &tmp)) { > + print_info("Duplicate redirect directories found:\n"); > + print_info("origin:%s current:%s latest:%s\n", > + chk.path, pathname, tmp); > + > + set_st_inconsistency(&status); > + } > + > + ovl_redirect_entry_add(pathname, sctx->dirtype, > + sctx->num, chk.path); > + > + /* Check and fix whiteout or opaque dir */ > + snprintf(redirect_rpath, sizeof(redirect_rpath), "%s%s", > + sctx->dirname, redirect); > + if (lstat(redirect_rpath, &rst) != 0) { > + if (errno != ENOENT && errno != ENOTDIR) { > + print_err(_("Cannot stat %s: %s\n"), > + redirect_rpath, strerror(errno)); > + goto out; > + } > + > + /* Found nothing, create a whiteout */ > + ret = ovl_create_whiteout(redirect_rpath); > + > + } else if (is_dir(&rst) && !ovl_is_opaque(redirect_rpath)) { > + /* Found a directory but not opaqued, fix opaque xattr */ > + ret = ovl_set_opaque(redirect_rpath); > + } > + > + goto out; > + } > + > +remove: > + /* Remove redirect xattr or ask user */ > + print_info(_("Invalid redirect xattr: %s "), pathname); > + if (!ask_question("Remove", 1)) > + goto out; > + > + ret = ovl_remove_redirect(pathname); > +out: > + free(redirect); > + return ret; > +} > + > +static struct scan_operations ovl_scan_ops = { > + .whiteout = ovl_check_whiteout, > + .opaque = ovl_check_opaque, > + .redirect = ovl_check_redirect, > +}; > + > +static void ovl_scan_clean(void) > +{ > + /* Clean redirect entry record */ > + vol_redirect_free(); > +} > + > +/* Scan upperdir and each lowerdirs, check and fix inconsistency */ > +int ovl_scan_fix(void) > +{ > + struct scan_ctx sctx; > + unsigned int i; > + int ret = 0; > + > + if (flags | FL_VERBOSE) > + print_info(_("Scan and fix: [whiteouts|opaque|redirectdir]\n")); > + > + /* Scan upper directory */ > + if (flags | FL_VERBOSE) > + print_info(_("Scan upper directory: %s\n"), upperdir); > + > + sctx.dirname = upperdir; > + sctx.dirlen = strlen(upperdir); > + sctx.dirtype = OVL_UPPER; > + > + ret = scan_dir(&sctx, &ovl_scan_ops); > + if (ret) > + goto out; > + > + /* Scan every lower directories */ > + for (i = 0; i < lower_num; i++) { > + if (flags | FL_VERBOSE) > + print_info(_("Scan upper directory: %s\n"), lowerdir[i]); > + > + sctx.dirname = lowerdir[i]; > + sctx.dirlen = strlen(lowerdir[i]); > + sctx.dirtype = OVL_LOWER; > + sctx.num = i; > + > + ret = scan_dir(&sctx, &ovl_scan_ops); > + if (ret) > + goto out; > + } > +out: > + ovl_scan_clean(); > + return ret; > +} > diff --git a/check.h b/check.h > new file mode 100644 > index 0000000..373ff3a > --- /dev/null > +++ b/check.h > @@ -0,0 +1,7 @@ > +#ifndef OVL_WHITECHECK_H > +#define OVL_WHITECHECK_H > + > +/* Scan upperdir and each lowerdirs, check and fix inconsistency */ > +int ovl_scan_fix(void); > + > +#endif /* OVL_WHITECHECK_H */ > diff --git a/common.c b/common.c > new file mode 100644 > index 0000000..4e77045 > --- /dev/null > +++ b/common.c > @@ -0,0 +1,95 @@ > +/* > + * > + * Common things for all utilities > + * > + * zhangyi (F) <yi.zhang@xxxxxxxxxx> - Sponsored by Huawei CR > + * > + */ > + > +#include <stdio.h> > +#include <string.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <stdarg.h> > +#include <errno.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include "common.h" > +#include "config.h" > + > +extern char *program_name; > + > +/* #define DEBUG 1 */ > +#ifdef DEBUG > +void print_debug(char *fmtstr, ...) > +{ > + va_list args; > + > + va_start(args, fmtstr); > + fprintf(stdout, "%s:[Debug]: ", program_name); > + vfprintf(stdout, fmtstr, args); > + va_end(args); > +} > +#else > +void print_debug (char *fmtstr, ...) {} > +#endif > + > +void print_info(char *fmtstr, ...) > +{ > + va_list args; > + > + va_start(args, fmtstr); > + vfprintf(stdout, fmtstr, args); > + va_end(args); > +} > + > +void print_err(char *fmtstr, ...) > +{ > + va_list args; > + > + va_start(args, fmtstr); > + fprintf(stderr, "%s:[Error]: ", program_name); > + vfprintf(stderr, fmtstr, args); > + va_end(args); > +} > + > +void *smalloc(size_t size) > +{ > + void *new = malloc(size); > + > + if (!new) { > + print_err(_("malloc error:%s\n"), strerror(errno)); > + exit(1); > + } > + > + memset(new, 0, size); > + return new; > +} > + > +void *srealloc(void *addr, size_t size) > +{ > + void *re = realloc(addr, size); > + > + if (!re) { > + print_err(_("malloc error:%s\n"), strerror(errno)); > + exit(1); > + } > + return re; > +} > + > +char *sstrdup(const char *src) > +{ > + char *dst = strdup(src); > + > + if (!dst) { > + print_err(_("strdup error:%s\n"), strerror(errno)); > + exit(1); > + } > + > + return dst; > +} > + > +void version(void) > +{ > + printf(_("Overlay utilities version %s\n"), PACKAGE_VERSION); > +} > diff --git a/common.h b/common.h > new file mode 100644 > index 0000000..c4707e7 > --- /dev/null > +++ b/common.h > @@ -0,0 +1,34 @@ > +#ifndef OVL_COMMON_H > +#define OVL_COMMON_H > + > +#ifndef __attribute__ > +# if !defined __GNUC__ || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ > +# define __attribute__(x) > +# endif > +#endif > + > +#ifdef USE_GETTEXT > +#include <libintl.h> > +#define _(x) gettext((x)) > +#else > +#define _(x) (x) > +#endif > + > +/* Print an error message */ > +void print_err(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); > + > +/* Print an info message */ > +void print_info(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); > + > +/* Print an debug message */ > +void print_debug(char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); > + > +/* Safety wrapper */ > +void *smalloc(size_t size); > +void *srealloc(void *addr, size_t size); > +char *sstrdup(const char *src); > + > +/* Print program version */ > +void version(void); > + > +#endif /* OVL_COMMON_H */ > diff --git a/config.h b/config.h > new file mode 100644 > index 0000000..deac089 > --- /dev/null > +++ b/config.h > @@ -0,0 +1,22 @@ > +#ifndef OVL_CONFIG_H > +#define OVL_CONFIG_H > + > +/* program version */ > +#define PACKAGE_VERSION "v1.0.0" > + > +/* overlay max lower stacks (the same to kernel overlayfs driver) */ > +#define OVL_MAX_STACK 500 > + > +/* File with mounted filesystems */ > +#define MOUNT_TAB "/proc/mounts" > + > +/* Name of overlay filesystem type */ > +#define OVERLAY_NAME "overlay" > +#define OVERLAY_NAME_OLD "overlayfs" > + > +/* Mount options */ > +#define OPT_LOWERDIR "lowerdir=" > +#define OPT_UPPERDIR "upperdir=" > +#define OPT_WORKDIR "workdir=" > + > +#endif > diff --git a/fsck.c b/fsck.c > new file mode 100644 > index 0000000..cbcb8e9 > --- /dev/null > +++ b/fsck.c > @@ -0,0 +1,179 @@ > +/* > + * > + * Utility to fsck overlay > + * > + * zhangyi (F) <yi.zhang@xxxxxxxxxx> - Sponsored by Huawei CR > + * > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <errno.h> > +#include <getopt.h> > +#include <libgen.h> > +#include <stdbool.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <linux/limits.h> > + > +#include "common.h" > +#include "config.h" > +#include "lib.h" > +#include "check.h" > +#include "mount.h" > + > +char *program_name; > + > +char **lowerdir; > +char upperdir[PATH_MAX] = {0}; > +char workdir[PATH_MAX] = {0}; > +unsigned int lower_num; > +int flags; /* user input option flags */ > +int status; /* fsck scan status */ > + > +/* Cleanup lower directories buf */ > +static void ovl_clean_lowerdirs(void) > +{ > + unsigned int i; > + > + for (i = 0; i < lower_num; i++) { > + free(lowerdir[i]); > + lowerdir[i] = NULL; > + lower_num = 0; > + } > + free(lowerdir); > + lowerdir = NULL; > +} > + > +static void usage(void) > +{ > + print_info(_("Usage:\n\t%s [-l lowerdir] [-u upperdir] [-w workdir] " > + "[-avhV]\n\n"), program_name); > + print_info(_("Options:\n" > + "-l, --lowerdir=LOWERDIR specify lower directories of overlayfs,\n" > + " multiple lower use ':' as separator\n" > + "-u, --upperdir=UPPERDIR specify upper directory of overlayfs\n" > + "-w, --workdir=WORKDIR specify work directory of overlayfs\n" > + "-a, --auto repair automatically (no questions)\n" > + "-v, --verbose print more messages of overlayfs\n" > + "-h, --help display this usage of overlayfs\n" > + "-V, --version display version information\n")); > + exit(1); > +} > + > +static void parse_options(int argc, char *argv[]) > +{ > + char *lowertemp; > + int c; > + int ret = 0; > + > + struct option long_options[] = { > + {"lowerdir", required_argument, NULL, 'l'}, > + {"upperdir", required_argument, NULL, 'u'}, > + {"workdir", required_argument, NULL, 'w'}, > + {"auto", no_argument, NULL, 'a'}, > + {"verbose", no_argument, NULL, 'v'}, > + {"version", no_argument, NULL, 'V'}, > + {"help", no_argument, NULL, 'h'}, > + {NULL, 0, NULL, 0} > + }; > + > + while ((c = getopt_long(argc, argv, "l:u:w:avVh", long_options, NULL)) != -1) { > + switch (c) { > + case 'l': > + lowertemp = strdup(optarg); > + ret = ovl_resolve_lowerdirs(lowertemp, &lowerdir, &lower_num); > + free(lowertemp); > + break; > + case 'u': > + if (realpath(optarg, upperdir)) { > + print_debug(_("Upperdir:%s\n"), upperdir); > + flags |= FL_UPPER; > + } else { > + print_err(_("Failed to resolve upperdir:%s\n"), optarg); > + ret = -1; > + } > + break; > + case 'w': > + if (realpath(optarg, workdir)) { > + print_debug(_("Workdir:%s\n"), workdir); > + flags |= FL_WORK; > + } else { > + print_err(_("Failed to resolve workdir:%s\n"), optarg); > + ret = -1; > + } > + break; > + case 'a': > + flags |= FL_AUTO; > + break; > + case 'v': > + flags |= FL_VERBOSE; > + break; > + case 'V': > + version(); > + return; > + case 'h': > + default: > + usage(); > + return; > + } > + > + if (ret) > + exit(1); > + } > + > + if (!lower_num || (!(flags & FL_UPPER) && lower_num == 1)) { > + print_info(_("Please specify correct lowerdirs and upperdir\n")); > + usage(); > + } > +} > + > +void fsck_status_check(int *val) > +{ > + if (status & OVL_ST_INCONSISTNECY) { > + *val |= FSCK_UNCORRECTED; > + print_info(_("Still have unexpected inconsistency!\n")); > + } > + > + if (status & OVL_ST_ABORT) { > + *val |= FSCK_ERROR; > + print_info(_("Cannot continue, aborting\n")); > + } > +} > + > +int main(int argc, char *argv[]) > +{ > + bool mounted; > + int err = 0; > + int exit_value = 0; > + > + program_name = basename(argv[0]); > + > + parse_options(argc, argv); > + > + /* Ensure overlay filesystem not mounted */ > + if ((err = ovl_check_mount(&mounted))) > + goto out; > + if (!mounted) { > + set_st_abort(&status); > + goto out; > + } > + > + /* Scan and fix */ > + if ((err = ovl_scan_fix())) > + goto out; > + > +out: > + fsck_status_check(&exit_value); > + ovl_clean_lowerdirs(); > + > + if (err) > + exit_value |= FSCK_ERROR; > + if (exit_value) > + print_info("WARNING: Filesystem check failed, may not clean\n"); > + else > + print_info("Filesystem clean\n"); > + > + return exit_value; > +} > diff --git a/lib.c b/lib.c > new file mode 100644 > index 0000000..a6832fe > --- /dev/null > +++ b/lib.c > @@ -0,0 +1,197 @@ > +/* > + * > + * Common things for all utilities > + * > + * zhangyi (F) <yi.zhang@xxxxxxxxxx> - Sponsored by Huawei CR > + * > + */ > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <errno.h> > +#include <string.h> > +#include <stdbool.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/xattr.h> > +#include <fts.h> > + > +#include "common.h" > +#include "lib.h" > + > +extern int flags; > +extern int status; > + > +static int ask_yn(const char *question, int def) > +{ > + char ans[16]; > + > + print_info(_("%s ? [%s]: \n"), question, def ? _("y") : _("n")); > + fflush(stdout); > + while (fgets(ans, sizeof(ans)-1, stdin)) { > + if (ans[0] == '\n') > + return def; > + else if (!strcasecmp(ans, "y\n") || !strcasecmp(ans, "yes\n")) > + return 1; > + else if (!strcasecmp(ans, "n\n") || !strcasecmp(ans, "no\n")) > + return 0; > + else > + print_info(_("Illegal answer. Please input y/n or yes/no:")); > + fflush(stdout); > + } > + return def; > +} > + > +/* Ask user */ > +int ask_question(const char *question, int def) > +{ > + if (flags & FL_AUTO) { > + print_info(_("%s? %s\n"), question, def ? _("y") : _("n")); > + return def; > + } > + > + return ask_yn(question, def); > +} > + > +ssize_t get_xattr(const char *pathname, const char *xattrname, > + char **value, bool *exist) > +{ > + char *buf = NULL; > + ssize_t ret; > + > + ret = getxattr(pathname, xattrname, NULL, 0); > + if (ret < 0) { > + if (errno == ENODATA || errno == ENOTSUP) { > + if (exist) > + *exist = false; > + return 0; > + } else { > + goto fail; > + } > + } > + > + /* Zero size value means xattr exist but value unknown */ > + if (exist) > + *exist = true; > + if (ret == 0 || !value) > + return 0; > + > + buf = smalloc(ret+1); > + ret = getxattr(pathname, xattrname, buf, ret); > + if (ret <= 0) > + goto fail2; > + > + buf[ret] = '\0'; > + *value = buf; > + return ret; > + > +fail2: > + free(buf); > +fail: > + print_err(_("Cannot getxattr %s %s: %s\n"), pathname, > + xattrname, strerror(errno)); > + return -1; > +} > + > +int set_xattr(const char *pathname, const char *xattrname, > + void *value, size_t size) > +{ > + int ret; > + > + ret = setxattr(pathname, xattrname, value, size, XATTR_CREATE); > + if (ret && ret != EEXIST) > + goto fail; > + > + if (ret == EEXIST) { > + ret = setxattr(pathname, xattrname, value, size, XATTR_REPLACE); > + if (ret) > + goto fail; > + } > + > + return 0; > +fail: > + print_err(_("Cannot setxattr %s %s: %s\n"), pathname, > + xattrname, strerror(errno)); > + return ret; > +} > + > +int remove_xattr(const char *pathname, const char *xattrname) > +{ > + int ret; > + if ((ret = removexattr(pathname, xattrname))) > + print_err(_("Cannot removexattr %s %s: %s\n"), pathname, > + xattrname, strerror(errno)); > + return ret; > +} > + > +static inline int __check_entry(struct scan_ctx *sctx, > + int (*do_check)(struct scan_ctx *)) > +{ > + return do_check ? do_check(sctx) : 0; > +} > + > +/* Scan specified directories and invoke callback */ > +int scan_dir(struct scan_ctx *sctx, struct scan_operations *sop) > +{ > + char *paths[2] = {(char *)sctx->dirname, NULL}; > + FTS *ftsp; > + FTSENT *ftsent; > + int ret = 0; > + > + ftsp = fts_open(paths, FTS_NOCHDIR | FTS_PHYSICAL, NULL); > + if (ftsp == NULL) { > + print_err(_("Failed to fts open %s:%s\n"), > + sctx->dirname, strerror(errno)); > + return -1; > + } > + > + while ((ftsent = fts_read(ftsp)) != NULL) { > + int err; > + > + print_debug(_("Scan:%-3s %2d %7jd %-40s %s\n"), > + (ftsent->fts_info == FTS_D) ? "d" : > + (ftsent->fts_info == FTS_DNR) ? "dnr" : > + (ftsent->fts_info == FTS_DP) ? "dp" : > + (ftsent->fts_info == FTS_F) ? "f" : > + (ftsent->fts_info == FTS_NS) ? "ns" : > + (ftsent->fts_info == FTS_SL) ? "sl" : > + (ftsent->fts_info == FTS_SLNONE) ? "sln" : > + (ftsent->fts_info == FTS_DEFAULT) ? "df" : "???", > + ftsent->fts_level, ftsent->fts_statp->st_size, > + ftsent->fts_path, ftsent->fts_name); > + > + > + /* Fillup base context */ > + sctx->pathname = ftsent->fts_path; > + sctx->pathlen = ftsent->fts_pathlen; > + sctx->filename = ftsent->fts_name; > + sctx->filelen = ftsent->fts_namelen; > + sctx->st = ftsent->fts_statp; > + > + switch (ftsent->fts_info) { > + case FTS_DEFAULT: > + /* Check whiteouts */ > + err = __check_entry(sctx, sop->whiteout); > + ret = (ret || !err) ? ret : err; > + break; > + case FTS_D: > + /* Check opaque and redirect */ > + err = __check_entry(sctx, sop->opaque); > + ret = (ret || !err) ? ret : err; > + > + err = __check_entry(sctx, sop->redirect); > + ret = (ret || !err) ? ret : err; > + break; > + case FTS_NS: > + case FTS_DNR: > + case FTS_ERR: > + print_err(_("Failed to fts read %s:%s\n"), > + ftsent->fts_path, strerror(ftsent->fts_errno)); > + goto out; > + } > + } > +out: > + fts_close(ftsp); > + return ret; > +} > diff --git a/lib.h b/lib.h > new file mode 100644 > index 0000000..463b263 > --- /dev/null > +++ b/lib.h > @@ -0,0 +1,73 @@ > +#ifndef OVL_LIB_H > +#define OVL_LIB_H > + > +/* Common return value */ > +#define FSCK_OK 0 /* No errors */ > +#define FSCK_NONDESTRUCT 1 /* File system errors corrected */ > +#define FSCK_REBOOT 2 /* System should be rebooted */ > +#define FSCK_UNCORRECTED 4 /* File system errors left uncorrected */ > +#define FSCK_ERROR 8 /* Operational error */ > +#define FSCK_USAGE 16 /* Usage or syntax error */ > +#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */ > +#define FSCK_LIBRARY 128 /* Shared library error */ > + > +/* Fsck status */ > +#define OVL_ST_INCONSISTNECY (1 << 0) > +#define OVL_ST_ABORT (1 << 1) > + > +/* Option flags */ > +#define FL_VERBOSE (1 << 0) /* verbose */ > +#define FL_UPPER (1 << 1) /* specify upper directory */ > +#define FL_WORK (1 << 2) /* specify work directory */ > +#define FL_AUTO (1 << 3) /* automactically scan dirs and repair */ > + > +/* Scan path type */ > +#define OVL_UPPER 0 > +#define OVL_LOWER 1 > +#define OVL_WORKER 2 > +#define OVL_PTYPE_MAX 3 > + > +/* Xattr */ > +#define OVL_OPAQUE_XATTR "trusted.overlay.opaque" > +#define OVL_REDIRECT_XATTR "trusted.overlay.redirect" > + > +/* Directories scan data struct */ > +struct scan_ctx { > + const char *dirname; /* upper/lower root dir */ > + size_t dirlen; /* strlen(dirlen) */ > + int dirtype; /* OVL_UPPER or OVL_LOWER */ > + int num; /* lower dir depth, lower type use only */ > + > + const char *pathname; /* file path from the root */ > + size_t pathlen; /* strlen(pathname) */ > + const char *filename; /* filename */ > + size_t filelen; /* strlen(filename) */ > + struct stat *st; /* file stat */ > +}; > + > +/* Directories scan callback operations struct */ > +struct scan_operations { > + int (*whiteout)(struct scan_ctx *); > + int (*opaque)(struct scan_ctx *); > + int (*redirect)(struct scan_ctx *); > +}; > + > +static inline void set_st_inconsistency(int *st) > +{ > + *st |= OVL_ST_INCONSISTNECY; > +} > + > +static inline void set_st_abort(int *st) > +{ > + *st |= OVL_ST_ABORT; > +} > + > +int scan_dir(struct scan_ctx *sctx, struct scan_operations *sop); > +int ask_question(const char *question, int def); > +ssize_t get_xattr(const char *pathname, const char *xattrname, > + char **value, bool *exist); > +int set_xattr(const char *pathname, const char *xattrname, > + void *value, size_t size); > +int remove_xattr(const char *pathname, const char *xattrname); > + > +#endif /* OVL_LIB_H */ > diff --git a/mount.c b/mount.c > new file mode 100644 > index 0000000..28ce8e5 > --- /dev/null > +++ b/mount.c > @@ -0,0 +1,319 @@ > +/* > + * > + * Check mounted overlay > + * > + * zhangyi (F) <yi.zhang@xxxxxxxxxx> - Sponsored by Huawei CR > + * > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <errno.h> > +#include <getopt.h> > +#include <libgen.h> > +#include <stdbool.h> > +#include <mntent.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <linux/limits.h> > + > +#include "common.h" > +#include "config.h" > +#include "lib.h" > +#include "check.h" > +#include "mount.h" > + > +struct ovl_mnt_entry { > + char *lowers; > + char **lowerdir; > + unsigned int lowernum; > + char upperdir[PATH_MAX]; > + char workdir[PATH_MAX]; > +}; > + > +/* Mount buf allocate a time */ > +#define ALLOC_NUM 16 > + > +extern char **lowerdir; > +extern char upperdir[]; > +extern char workdir[]; > +extern unsigned int lower_num; > + > +/* > + * Split directories to individual one. > + * (copied from linux kernel, see fs/overlayfs/super.c) > + */ It would be nice if such functions could be maintained in a file/dir that is synced with the kernel, like the xfs/lib files. For the first release we don't really have to sync this file with the kernel, but at least keep them at a specific file/dir and maybe the kernel driver will follow up later. > +static unsigned int ovl_split_lowerdirs(char *lower) > +{ > + unsigned int ctr = 1; > + char *s, *d; > + > + for (s = d = lower;; s++, d++) { > + if (*s == '\\') { > + s++; > + } else if (*s == ':') { > + *d = '\0'; > + ctr++; > + continue; > + } > + *d = *s; > + if (!*s) > + break; > + } > + return ctr; > +} > + > +/* Resolve each lower directories and check the validity */ > +int ovl_resolve_lowerdirs(char *loweropt, char ***lowerdir, > + unsigned int *lowernum) > +{ > + unsigned int num; > + char **dirs; > + char *p; > + int i; > + > + num = ovl_split_lowerdirs(loweropt); > + if (num > OVL_MAX_STACK) { > + print_err(_("Too many lower directories:%u, max:%u\n"), > + num, OVL_MAX_STACK); > + return -1; > + } > + > + dirs = smalloc(sizeof(char *) * num); > + > + p = loweropt; > + for (i = 0; i < num; i++) { > + dirs[i] = smalloc(PATH_MAX); > + if (!realpath(p, dirs[i])) { > + print_err(_("Failed to resolve lowerdir:%s:%s\n"), > + p, strerror(errno)); > + goto err; > + } > + print_debug(_("Lowerdir %u:%s\n"), i, dirs[i]); > + p = strchr(p, '\0') + 1; > + } > + > + *lowerdir = dirs; > + *lowernum = num; > + > + return 0; > +err: > + for (i--; i >= 0; i--) > + free(dirs[i]); > + free(dirs); > + *lowernum = 0; > + return -1; > +} > + > +/* > + * Split and return next opt. > + * (copied from linux kernel, see fs/overlayfs/super.c) > + */ > +static char *ovl_next_opt(char **s) > +{ > + char *sbegin = *s; > + char *p; > + > + if (sbegin == NULL) > + return NULL; > + > + for (p = sbegin; *p; p++) { > + if (*p == '\\') { > + p++; > + if (!*p) > + break; > + } else if (*p == ',') { > + *p = '\0'; > + *s = p + 1; > + return sbegin; > + } > + } > + *s = NULL; > + return sbegin; > +} > + > +/* > + * Split and parse opt to each directories. > + * > + * Note: FIXME: We cannot distinguish mounted directories if overlayfs was > + * mounted use relative path, so there may have misjudgment. > + */ > +static int ovl_parse_opt(char *opt, struct ovl_mnt_entry *entry) > +{ > + char tmp[PATH_MAX] = {0}; > + char *p; > + int len; > + int ret; > + int i; > + > + while ((p = ovl_next_opt(&opt)) != NULL) { > + if (!*p) > + continue; > + > + if (!strncmp(p, OPT_UPPERDIR, strlen(OPT_UPPERDIR))) { > + len = strlen(p) - strlen(OPT_UPPERDIR) + 1; > + strncpy(tmp, p+strlen(OPT_UPPERDIR), len); > + > + if (!realpath(tmp, entry->upperdir)) { > + print_err(_("Faile to resolve path:%s:%s\n"), > + tmp, strerror(errno)); > + ret = -1; > + goto errout; > + } > + } else if (!strncmp(p, OPT_LOWERDIR, strlen(OPT_LOWERDIR))) { > + len = strlen(p) - strlen(OPT_LOWERDIR) + 1; > + entry->lowers = smalloc(len); > + strncpy(entry->lowers, p+strlen(OPT_LOWERDIR), len); > + > + if ((ret = ovl_resolve_lowerdirs(entry->lowers, > + &entry->lowerdir, &entry->lowernum))) > + goto errout; > + > + } else if (!strncmp(p, OPT_WORKDIR, strlen(OPT_WORKDIR))) { > + len = strlen(p) - strlen(OPT_WORKDIR) + 1; > + strncpy(tmp, p+strlen(OPT_WORKDIR), len); > + > + if (!realpath(tmp, entry->workdir)) { > + print_err(_("Faile to resolve path:%s:%s\n"), > + tmp, strerror(errno)); > + ret = -1; > + goto errout; > + } > + } > + } > + > +errout: > + if (entry->lowernum) { > + for (i = 0; i < entry->lowernum; i++) > + free(entry->lowerdir[i]); > + free(entry->lowerdir); > + entry->lowerdir = NULL; > + entry->lowernum = 0; > + } > + free(entry->lowers); > + entry->lowers = NULL; > + > + return ret; > +} > + > +/* Scan current mounted overlayfs and get used underlying directories */ > +static int ovl_scan_mount_init(struct ovl_mnt_entry **ovl_mnt_entries, > + int *ovl_mnt_count) > +{ > + struct ovl_mnt_entry *mnt_entries; > + struct mntent *mnt; > + FILE *fp; > + char *opt; > + int allocated, num = 0; > + > + fp = setmntent(MOUNT_TAB, "r"); > + if (!fp) { > + print_err(_("Fail to setmntent %s:%s\n"), > + MOUNT_TAB, strerror(errno)); > + return -1; > + } > + > + allocated = ALLOC_NUM; > + mnt_entries = smalloc(sizeof(struct ovl_mnt_entry) * allocated); > + > + while ((mnt = getmntent(fp))) { > + if (!strcmp(mnt->mnt_type, OVERLAY_NAME) || > + !strcmp(mnt->mnt_type, OVERLAY_NAME_OLD)) { > + > + opt = sstrdup(mnt->mnt_opts); > + if (ovl_parse_opt(opt, &mnt_entries[num])) { > + free(opt); > + continue; > + } > + > + num++; > + if (num % ALLOC_NUM == 0) { > + allocated += ALLOC_NUM; > + mnt_entries = srealloc(mnt_entries, > + sizeof(struct ovl_mnt_entry) * allocated); > + } > + > + free(opt); > + } > + } > + > + *ovl_mnt_entries = mnt_entries; > + *ovl_mnt_count = num; > + > + endmntent(fp); > + return 0; > +} > + > +static void ovl_scan_mount_exit(struct ovl_mnt_entry *ovl_mnt_entries, > + int ovl_mnt_count) > +{ > + int i,j; > + > + for (i = 0; i < ovl_mnt_count; i++) { > + for (j = 0; j < ovl_mnt_entries[i].lowernum; j++) > + free(ovl_mnt_entries[i].lowerdir[j]); > + free(ovl_mnt_entries[i].lowerdir); > + free(ovl_mnt_entries[i].lowers); > + } > + free(ovl_mnt_entries); > +} > + > +/* > + * Scan every mounted filesystem, check the overlay directories want > + * to check is already mounted. Check and fix an online overlay is not > + * allowed. > + * > + * Note: fsck may modify lower layers, so even match only one directory > + * is triggered as mounted. > + */ > +int ovl_check_mount(bool *pass) > +{ > + struct ovl_mnt_entry *ovl_mnt_entries; > + int ovl_mnt_entry_count; > + char *mounted_path; > + bool mounted; > + int i,j,k; > + int ret; > + > + ret = ovl_scan_mount_init(&ovl_mnt_entries, &ovl_mnt_entry_count); > + if (ret) > + return ret; > + > + /* Only check hard matching */ > + for (i = 0; i < ovl_mnt_entry_count; i++) { > + /* Check lower */ > + for (j = 0; j < ovl_mnt_entries[i].lowernum; j++) { > + for (k = 0; k < lower_num; k++) { > + if (!strcmp(lowerdir[k], > + ovl_mnt_entries[i].lowerdir[j])) { > + mounted_path = lowerdir[k]; > + mounted = true; > + goto out; > + } > + } > + } > + > + /* Check upper */ > + if (!(strcmp(upperdir, ovl_mnt_entries[i].upperdir))) { > + mounted_path = upperdir; > + mounted = true; > + goto out; > + } > + > + /* Check worker */ > + if (workdir[0] != '\0' && !(strcmp(workdir, ovl_mnt_entries[i].workdir))) { > + mounted_path = workdir; > + mounted = true; > + goto out; > + } > + } > +out: > + ovl_scan_mount_exit(ovl_mnt_entries, ovl_mnt_entry_count); > + > + if (mounted) > + print_info(_("Dir %s is mounted\n"), mounted_path); > + *pass = !mounted; > + > + return 0; > +} > diff --git a/mount.h b/mount.h > new file mode 100644 > index 0000000..8a3762d > --- /dev/null > +++ b/mount.h > @@ -0,0 +1,8 @@ > +#ifndef OVL_MOUNT_H > +#define OVL_MOUNT_H > + > +int ovl_resolve_lowerdirs(char *loweropt, char ***lowerdir, > + unsigned int *lowernum); > +int ovl_check_mount(bool *mounted); > + > +#endif /* OVL_MOUNT_H */ > diff --git a/test/README.md b/test/README.md > new file mode 100644 > index 0000000..71dbad8 > --- /dev/null > +++ b/test/README.md > @@ -0,0 +1,12 @@ > +fsck.overlay test > +================= > + > +This test cases simulate different inconsistency of overlay filesystem > +underlying directories, test fsck.overlay can repair them correctly or not. > + > +USAGE > +===== > + > +1. Check "local.config", modify config value to appropriate one. > +2. ./auto_test.sh > +3. After testing, the result file and log file created in the result directory. This really sounds like a mini fork of xfstests. I'd prefer if those tests went into xfstests and used fsck.overlay either as an external program (like the rest of fsck.*) or as in-house test helper. > diff --git a/test/auto_test.sh b/test/auto_test.sh > new file mode 100755 > index 0000000..8248e24 > --- /dev/null > +++ b/test/auto_test.sh > @@ -0,0 +1,46 @@ > +#!/bin/bash > + > +. ./src/prepare.sh > + > +echo "***************" > + > +# 1. Test whiteout > +echo -e "1.Test Whiteout\n" > +$SOURCE_DIR/whiteout_test.sh >> $LOG_FILE 2>&1 > +if [ $? -ne 0 ]; then > + echo -e "Result:\033[31m Fail\033[0m\n" > + RESULT="Fail" > +else > + echo -e "Result:\033[32m Pass\033[0m\n" > + RESULT="Pass" > +fi > + > +echo -e "Test whiteout: $RESULT" >> $RESOULT_FILE > + > +# 2. Test opaque dir > +echo -e "2.Test opaque directory\n" > +$SOURCE_DIR/opaque_test.sh >> $LOG_FILE 2>&1 > +if [ $? -ne 0 ]; then > + echo -e "Result:\033[31m Fail\033[0m\n" > + RESULT="Fail" > +else > + echo -e "Result:\033[32m Pass\033[0m\n" > + RESULT="Pass" > +fi > + > +echo -e "Test opaque directory: $RESULT" >> $RESOULT_FILE > + > +# 3. Test redirect dir > +echo -e "3.Test redirect direcotry\n" > +$SOURCE_DIR/redirect_test.sh >> $LOG_FILE 2>&1 > +if [ $? -ne 0 ]; then > + echo -e "Result:\033[31m Fail\033[0m\n" > + RESULT="Fail" > +else > + echo -e "Result:\033[32m Pass\033[0m\n" > + RESULT="Pass" > +fi > + > +echo -e "Test redirect direcotry: $RESULT" >> $RESOULT_FILE > + > +echo "***************" > diff --git a/test/clean.sh b/test/clean.sh > new file mode 100755 > index 0000000..80f360b > --- /dev/null > +++ b/test/clean.sh > @@ -0,0 +1,6 @@ > +#!/bin/bash > + > +. ./src/prepare.sh > + > +rm -rf $RESULT_DIR > +rm -rf $TEST_DIR > diff --git a/test/local.config b/test/local.config > new file mode 100644 > index 0000000..990395f > --- /dev/null > +++ b/test/local.config > @@ -0,0 +1,16 @@ > +# Config > +export OVL_FSCK=fsck.overlay > +export TEST_DIR=/mnt/test > + > +# Base environment > +export PWD=`pwd` > +export RESULT_DIR=$PWD/result > +export SOURCE_DIR=$PWD/src > + > +# Overlayfs config > +export LOWER_DIR="lower" > +export UPPER_DIR="upper" > +export WORK_DIR="worker" worker? this is a strange name for work dir location.. it does not have the same meaning in English as upp-er and low-er. > +export MERGE_DIR="merge" > +export LOWER_DIR1="lower1" > +export WORK_DIR1="worker1" > diff --git a/test/src/opaque_test.sh b/test/src/opaque_test.sh > new file mode 100755 > index 0000000..7cb5030 > --- /dev/null > +++ b/test/src/opaque_test.sh > @@ -0,0 +1,144 @@ > +#!/bin/bash > + > +. ./local.config > + > +__clean() > +{ > + rm -rf $TEST_DIR/* > + cd $PWD > +} > + > +OVL_OPAQUE_XATTR="trusted.overlay.opaque" > +OVL_OPAQUE_XATTR_VAL="y" > +RET=0 > + > +cd $TEST_DIR > +rm -rf * > +mkdir $LOWER_DIR $LOWER_DIR1 $UPPER_DIR $WORK_DIR > + > +# 1.Test lower invalid opaque directory > +echo "***** 1.Test lower invalid opaque directory *****" > +mkdir $LOWER_DIR/testdir > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $LOWER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $LOWER_DIR/testdir > +if [[ $? -eq 0 ]];then > + echo "ERROR: lower's invalid opaque xattr not remove" > + RET=$(($RET+1)) > +fi > +rm -rf $LOWER_DIR/* > + > +# 2.Test upper invalid opaque directory > +echo "***** 2.Test upper invalid opaque directory *****" > +mkdir -p $UPPER_DIR/testdir0/testdir1 > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $UPPER_DIR/testdir0/testdir1 > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $UPPER_DIR/testdir0/testdir1 > +if [[ $? -eq 0 ]];then > + echo "ERROR: upper's invalid opaque xattr not remove" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > + > +# 3.Test upper opaque direcotry in merged directory > +echo "***** 3.Test upper opaque direcotry in merged directory *****" > +mkdir $UPPER_DIR/testdir > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $UPPER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's opaque xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > + > +# 4.Test upper opaque direcotry cover lower file > +echo "***** 4.Test upper opaque direcotry cover lower file *****" > +mkdir $UPPER_DIR/testdir > +mkdir $LOWER_DIR/testdir > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $UPPER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's opaque xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > + > +# 5.Test upper opaque direcotry cover lower directory > +echo "***** 5.Test upper opaque direcotry cover lower directory *****" > +mkdir $UPPER_DIR/testdir > +touch $LOWER_DIR/testdir > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $UPPER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's opaque xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > + > +# 6.Test lower invalid opaque directory in middle layer > +echo "***** 6.Test lower invalid opaque directory in middle layer *****" > +mkdir $LOWER_DIR/testdir0/testdir1 > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $LOWER_DIR/testdir0/testdir1 > +$OVL_FSCK -a -l $LOWER_DIR:$LOWER_DIR1 -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $LOWER_DIR/testdir0/testdir1 > +if [[ $? -eq 0 ]];then > + echo "ERROR: middle's opaque xattr not removed" > + RET=$(($RET+1)) > +fi > +rm -rf $LOWER_DIR/* > + > +# 7.Test lower invalid opaque directory in bottom layer > +echo "***** 7.Test lower invalid opaque directory in bottom layer *****" > +mkdir $LOWER_DIR1/testdir0/testdir1 > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $LOWER_DIR1/testdir0/ > +$OVL_FSCK -a -l $LOWER_DIR:$LOWER_DIR1 -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $LOWER_DIR1/testdir0/testdir1 > +if [[ $? -eq 0 ]];then > + echo "ERROR: middle's opaque xattr not removed" > + RET=$(($RET+1)) > +fi > +rm -rf $LOWER_DIR1/* > + > +# 8.Test middle opaque direcotry cover bottom directory > +echo "***** 8.Test middle opaque direcotry cover bottom directory *****" > +mkdir $LOWER_DIR1/testdir > +mkdir $LOWER_DIR/testdir > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $LOWER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR:$LOWER_DIR1 -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $LOWER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: middle's opaque xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +rm -rf $LOWER_DIR1/* > +rm -rf $LOWER_DIR/* > + > +# 9.Test double opaque direcotry in middle and upper layer > +echo "***** 9.Test double opaque direcotry in middle and upper layer *****" > +mkdir $LOWER_DIR1/testdir > +mkdir $LOWER_DIR/testdir > +mkdir $UPPER_DIR/testdir > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $LOWER_DIR/testdir > +setfattr -n $OVL_OPAQUE_XATTR -v $OVL_OPAQUE_XATTR_VAL $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR:$LOWER_DIR1 -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_OPAQUE_XATTR $LOWER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: middle's opaque xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +getfattr -n $OVL_OPAQUE_XATTR $UPPER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's opaque xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +rm -rf $LOWER_DIR1/* > +rm -rf $LOWER_DIR/* > +rm -rf $UPPER_DIR/* > + > +__clean > +exit $RET > diff --git a/test/src/prepare.sh b/test/src/prepare.sh > new file mode 100755 > index 0000000..138539a > --- /dev/null > +++ b/test/src/prepare.sh > @@ -0,0 +1,15 @@ > +#!/bin/bash > + > +. ./local.config > + > +NOW=`date +"%Y-%m-%d-%H-%M-%S"` > +LOG_FILE=$RESULT_DIR/LOG_${NOW}.log > +RESOULT_FILE=$RESULT_DIR/RESULT_${NOW}.out > + > +# creat test base dir > +if [ ! -d "$TEST_DIR" ]; then > + mkdir -p $TEST_DIR > +fi > + > +# creat result dir > +mkdir -p $RESULT_DIR > diff --git a/test/src/redirect_test.sh b/test/src/redirect_test.sh > new file mode 100755 > index 0000000..be2ce80 > --- /dev/null > +++ b/test/src/redirect_test.sh > @@ -0,0 +1,163 @@ > +#!/bin/bash > + > +. ./local.config > + > +__clean() > +{ > + rm -rf $TEST_DIR/* > + cd $PWD > +} > + > +OVL_REDIRECT_XATTR="trusted.overlay.redirect" > +OVL_OPAQUE_XATTR="trusted.overlay.opaque" > +RET=0 > + > +cd $TEST_DIR > +rm -rf * > +mkdir $LOWER_DIR $LOWER_DIR1 $UPPER_DIR $WORK_DIR > + > +# 1.Test no exist redirect origin > +echo "***** 1.Test no exist redirect origin *****" > +mkdir $UPPER_DIR/testdir > +setfattr -n $OVL_REDIRECT_XATTR -v "xxx" $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_REDIRECT_XATTR $UPPER_DIR/testdir > +if [[ $? -eq 0 ]];then > + echo "ERROR: upper's redirect xattr not remove" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > + > +# 2.Test redirect origin is file > +echo "***** 2.Test redirect origin is file *****" > +mkdir $UPPER_DIR/testdir > +touch $LOWER_DIR/origin > +setfattr -n $OVL_REDIRECT_XATTR -v "origin" $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_REDIRECT_XATTR $UPPER_DIR/testdir > +if [[ $? -eq 0 ]];then > + echo "ERROR: upper's redirect xattr not remove" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > + > +# 3.Test valid redirect xattr > +echo "***** 3.Test valid redirect xattr *****" > +mkdir $UPPER_DIR/testdir > +mknod $UPPER_DIR/origin c 0 0 > +mkdir $LOWER_DIR/origin > +setfattr -n $OVL_REDIRECT_XATTR -v "origin" $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_REDIRECT_XATTR $UPPER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's redirect xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > + > +# 4.Test valid redirect xattr start from root > +echo "***** 4.Test valid redirect xattr start from root *****" > +mkdir -p $UPPER_DIR/testdir0/testdir1 > +mknod $UPPER_DIR/origin c 0 0 > +mkdir $LOWER_DIR/origin > +setfattr -n $OVL_REDIRECT_XATTR -v "/origin" $UPPER_DIR/testdir0/testdir1 > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_REDIRECT_XATTR $UPPER_DIR/testdir0/testdir1 > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's redirect xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > + > +# 5.Test missing whiteout in redirect parent directory > +echo "***** 5.Test missing whiteout in redirect parent directory *****" > +mkdir $UPPER_DIR/testdir > +mkdir $LOWER_DIR/origin > +setfattr -n $OVL_REDIRECT_XATTR -v "origin" $UPPER_DIR/testdir > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_REDIRECT_XATTR $UPPER_DIR/testdir > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's redirect xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +if [ ! -e "$UPPER_DIR/origin" ];then > + echo "ERROR: upper's missing whiteout not create" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $UPPER_DIR/* > + > +# 6.Test missing opaque in redirect parent directory > +echo "***** 6.Test missing opaque in redirect parent directory *****" > +mkdir -p $UPPER_DIR/testdir0/testdir1 > +mkdir $UPPER_DIR/origin > +mkdir $LOWER_DIR/origin > +setfattr -n $OVL_REDIRECT_XATTR -v "/origin" $UPPER_DIR/testdir0/testdir1 > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_REDIRECT_XATTR $UPPER_DIR/testdir0/testdir1 > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's redirect xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +getfattr -n $OVL_OPAQUE_XATTR $UPPER_DIR/origin > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's missing opaque not set" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > + > +# 7.Test duplicate redirect directory in one layer > +echo "***** 7.Test duplicate redirect directory in one layer *****" > +mkdir $UPPER_DIR/testdir0 > +mkdir $UPPER_DIR/testdir1 > +mknod $UPPER_DIR/origin c 0 0 > +mkdir $LOWER_DIR/origin > +setfattr -n $OVL_REDIRECT_XATTR -v "origin" $UPPER_DIR/testdir0 > +setfattr -n $OVL_REDIRECT_XATTR -v "origin" $UPPER_DIR/testdir1 > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +if [[ $? -eq 0 ]];then > + echo "ERROR: fsck check incorrect pass" > + RET=$(($RET+1)) > +fi > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > + > +# 8.Test duplicate redirect directory in different layer > +echo "***** 8.Test duplicate redirect directory in different layer *****" > +mkdir $UPPER_DIR/testdir0 > +mkdir $LOWER_DIR/testdir1 > +mkdir $LOWER_DIR1/origin > +setfattr -n $OVL_REDIRECT_XATTR -v "origin" $UPPER_DIR/testdir0 > +setfattr -n $OVL_REDIRECT_XATTR -v "origin" $LOWER_DIR/testdir1 > +$OVL_FSCK -a -l $LOWER_DIR:$LOWER_DIR1 -u $UPPER_DIR -w $WORK_DIR > +getfattr -n $OVL_REDIRECT_XATTR $UPPER_DIR/testdir0 > +if [[ $? -ne 0 ]];then > + echo "ERROR: upper's redirect xattr incorrect removed" > + RET=$(($RET+1)) > +fi > +getfattr -n $OVL_REDIRECT_XATTR $LOWER_DIR/testdir1 > +if [[ $? -ne 0 ]];then > + echo "ERROR: lower's redirect xattr incorrect removed" > + RET=$(($RET+1)) > +fi > + > +if [ ! -e "$LOWER_DIR/origin" ];then > + echo "ERROR: upper's missing whiteout not create" > + RET=$(($RET+1)) > +fi > +if [ ! -e "$LOWER_DIR/origin" ];then > + echo "ERROR: lower's missing whiteout not create" > + RET=$(($RET+1)) > +fi > + > +rm -rf $UPPER_DIR/* > +rm -rf $LOWER_DIR/* > +rm -rf $LOWER_DIR1/* > + > +__clean > + > +exit $RET > diff --git a/test/src/whiteout_test.sh b/test/src/whiteout_test.sh > new file mode 100755 > index 0000000..c0e1555 > --- /dev/null > +++ b/test/src/whiteout_test.sh > @@ -0,0 +1,63 @@ > +#!/bin/bash > + > +. ./local.config > + > +__clean() > +{ > + rm -rf $TEST_DIR/* > + cd $PWD > +} > + > +RET=0 > + > +cd $TEST_DIR > +rm -rf * > +mkdir $LOWER_DIR $LOWER_DIR1 $UPPER_DIR $WORK_DIR > + > +# 1.Test lower orphan whiteout > +echo "***** 1.Test lower orphan whiteout *****" > +mknod $LOWER_DIR/foo c 0 0 > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +if [ -e "$LOWER_DIR/foo" ];then > + echo "lower's whiteout not remove" > + RET=$(($RET+1)) > +fi > +rm -f $LOWER_DIR/* > + > +# 2.Test upper orphan whiteout > +echo "***** 2.Test upper orphan whiteout *****" > +mknod $UPPER_DIR/foo c 0 0 > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +if [ -e "$UPPER_DIR/foo" ];then > + echo "upper's whiteout not remove" > + RET=$(($RET+1)) > +fi > +rm -f $UPPER_DIR/* > + > +# 3.Test upper inuse whiteout > +echo "***** 3.Test upper inuse whiteout *****" > +touch $LOWER_DIR/foo > +mknod $UPPER_DIR/foo c 0 0 > +$OVL_FSCK -a -l $LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +if [ ! -e "$UPPER_DIR/foo" ];then > + echo "upper's whiteout incorrect remove" > + RET=$(($RET+1)) > +fi > +rm -f $UPPER_DIR/* > +rm -f $LOWER_DIR/* > + > +# 4.Test lower inuse whiteout > +echo "***** 4.Test lower inuse whiteout *****" > +touch $LOWER_DIR/foo > +mknod $LOWER_DIR1/foo c 0 0 > +$OVL_FSCK -a -l $LOWER_DIR1:$LOWER_DIR -u $UPPER_DIR -w $WORK_DIR > +if [ ! -e "$LOWER_DIR1/foo" ];then > + echo "lower's whiteout incorrect remove" > + RET=$(($RET+1)) > +fi > +rm -f $LOWER_DIR1/* > +rm -f $LOWER_DIR/* > + > +__clean > + > +exit $RET > -- > 2.9.5 > -- To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html