On 2017/11/18 1:13, Amir Goldstein Wrote: > [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. > Hi Amir, Thanks a lot for review and your suggestions. >> >> 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 > I found that e2fsprogs put tests in the e2fsprogs repository, so I write it and put in the same place. Now I notice that all xfs test cases put together in xfstests, so It's also a good choice, not sure which one is better. How about Eryu's opinion ? > 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. > Can we applay for a repository in git.kernel.org like xfsprogs-dev and e2fsprogs ? >> >> 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. Yes, will fix 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. > Good suggesttion, will consider it later. >> 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 > Do you mean the following case: upper's dir_rd cover lower1's redirect dir_rd ? Upper: dir_rd(redurected,opaque) dir2(whiteout) Lower1: dir(whiteout) dir_rd(redurected) dir2 Lower0: dir If so, I think my program handled this case already. >> + >> +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. > Yes, I tried to use origin xattr and get the ovl_fh. We can put the redirect information into ovl_fh to let fsck fix redirect latter. > >> + >> +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. > will fix it. >> + * >> + */ >> + >> +#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. > It's a good idea, will change it. >> + 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. > will consider about it, thx. >> +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. > sorry, clerical error, will fix it. >> +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