On 2017/11/18 2:39, Darrick J. Wong Wrote: > On Fri, Nov 17, 2017 at 07:13:23PM +0200, 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. >> >>> >>> 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. > > If it's e2fsck conventions you want, then consider that e2fsck has three > action options -- -n (fix nothing), -y (fix everything), and -p (fix the > easy stuff that doesn't require interaction); and -a maps to -p. > > In other words, think about whether or not repairs to overlayfs could > someday be classified into easy fixes vs. difficult fixes. Even if that > never happens, if you're going to mirror e2fsck then you might as well > mirror the exact options behavior. > Thanks for your suggestion, I will add -n, -y and -p options and distinguish between -y and -p. Thanks, Yi. > >> >>> -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 fstests" in >> the body of a message to majordomo@xxxxxxxxxxxxxxx >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > . > -- 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