Re: [fsck.overlay RFC PATCH] overlay: add fsck utility

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux