[patch] fix sys_mknodat breakage from r/o bind mounts - Kernel

This is a discussion on [patch] fix sys_mknodat breakage from r/o bind mounts - Kernel ; From: Miklos Szeredi mknod("foo", 0644) returns EINVAL, even though it should succeed. The responsible patch is this: r-o-bind-mounts-sys_mknodat-elevate-write-count-for-vfs_mknod-create.patch Signed-off-by: Miklos Szeredi --- Index: linux/fs/namei.c ================================================== ================= --- linux.orig/fs/namei.c 2007-09-26 00:36:15.000000000 +0200 +++ linux/fs/namei.c 2007-09-26 00:51:19.000000000 +0200 @@ -1984,7 +1984,7 @@ ...

+ Reply to Thread
Results 1 to 4 of 4

Thread: [patch] fix sys_mknodat breakage from r/o bind mounts

  1. [patch] fix sys_mknodat breakage from r/o bind mounts

    From: Miklos Szeredi

    mknod("foo", 0644) returns EINVAL, even though it should succeed.

    The responsible patch is this:

    r-o-bind-mounts-sys_mknodat-elevate-write-count-for-vfs_mknod-create.patch

    Signed-off-by: Miklos Szeredi
    ---

    Index: linux/fs/namei.c
    ================================================== =================
    --- linux.orig/fs/namei.c 2007-09-26 00:36:15.000000000 +0200
    +++ linux/fs/namei.c 2007-09-26 00:51:19.000000000 +0200
    @@ -1984,7 +1984,7 @@ asmlinkage long sys_mknodat(int dfd, con
    goto out_dput;
    }
    if (!S_ISREG(mode) && !S_ISCHR(mode) && !S_ISBLK(mode) &&
    - !S_ISFIFO(mode) && !S_ISSOCK(mode) && mode != 0) {
    + !S_ISFIFO(mode) && !S_ISSOCK(mode) && (mode & S_IFMT) != 0) {
    error = -EINVAL;
    goto out_dput;
    }

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  2. Re: [patch] fix sys_mknodat breakage from r/o bind mounts

    On Wed, 2007-09-26 at 00:58 +0200, Miklos Szeredi wrote:
    > From: Miklos Szeredi
    >
    > mknod("foo", 0644) returns EINVAL, even though it should succeed.


    Just to clarify, this is for when we use mknod to create normal,
    non-device files, right? Your fix looks very correct to me.

    I'm curious, do you have a normal program that uses mknod(2) this way?
    I'd like to add it to my test scripts.

    Acked-by: Dave Hansen

    -- Dave

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  3. Re: [patch] fix sys_mknodat breakage from r/o bind mounts

    > > From: Miklos Szeredi
    > >
    > > mknod("foo", 0644) returns EINVAL, even though it should succeed.

    >
    > Just to clarify, this is for when we use mknod to create normal,
    > non-device files, right?


    Yes.

    > I'm curious, do you have a normal program that uses mknod(2) this way?
    > I'd like to add it to my test scripts.


    I have a test program in the fuse CVS. Here it is:

    ----
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include


    static char testfile[1024];
    static char testfile2[1024];
    static char testdir[1024];
    static char testdir2[1024];
    static char testname[256];
    static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
    static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
    static const char *testdir_files[] = { "f1", "f2", NULL};
    static char zerodata[4096];
    static int testdatalen = sizeof(testdata) - 1;
    static int testdata2len = sizeof(testdata2) - 1;

    #define MAX_ENTRIES 1024

    static void test_perror(const char *func, const char *msg)
    {
    fprintf(stderr, "[%s] %s() - %s: %s\n", testname, func, msg,
    strerror(errno));
    }

    static void test_error(const char *func, const char *msg, ...)
    __attribute__ ((format (printf, 2, 3)));

    static void start_test(const char *fmt, ...)
    __attribute__ ((format (printf, 1, 2)));

    static void test_error(const char *func, const char *msg, ...)
    {
    va_list ap;
    fprintf(stderr, "[%s] %s() - ", testname, func);
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
    fprintf(stderr, "\n");
    }

    static void success(void)
    {
    fprintf(stderr, "[%s] OK\n", testname);
    }

    static void start_test(const char *fmt, ...)
    {
    va_list ap;
    va_start(ap, fmt);
    vsprintf(testname, fmt, ap);
    va_end(ap);
    }

    #define PERROR(msg) test_perror(__FUNCTION__, msg)
    #define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)

    static int check_size(const char *path, int len)
    {
    struct stat stbuf;
    int res = stat(path, &stbuf);
    if (res == -1) {
    PERROR("stat");
    return -1;
    }
    if (stbuf.st_size != len) {
    ERROR("length %u instead of %u", (int) stbuf.st_size, (int) len);
    return -1;
    }
    return 0;
    }

    static int fcheck_size(int fd, int len)
    {
    struct stat stbuf;
    int res = fstat(fd, &stbuf);
    if (res == -1) {
    PERROR("fstat");
    return -1;
    }
    if (stbuf.st_size != len) {
    ERROR("length %u instead of %u", (int) stbuf.st_size, (int) len);
    return -1;
    }
    return 0;
    }

    static int check_type(const char *path, mode_t type)
    {
    struct stat stbuf;
    int res = lstat(path, &stbuf);
    if (res == -1) {
    PERROR("lstat");
    return -1;
    }
    if ((stbuf.st_mode & S_IFMT) != type) {
    ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type);
    return -1;
    }
    return 0;
    }

    static int fcheck_type(int fd, mode_t type)
    {
    struct stat stbuf;
    int res = fstat(fd, &stbuf);
    if (res == -1) {
    PERROR("fstat");
    return -1;
    }
    if ((stbuf.st_mode & S_IFMT) != type) {
    ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type);
    return -1;
    }
    return 0;
    }

    static int check_mode(const char *path, mode_t mode)
    {
    struct stat stbuf;
    int res = lstat(path, &stbuf);
    if (res == -1) {
    PERROR("lstat");
    return -1;
    }
    if ((stbuf.st_mode & 07777) != mode) {
    ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode);
    return -1;
    }
    return 0;
    }

    static int fcheck_mode(int fd, mode_t mode)
    {
    struct stat stbuf;
    int res = fstat(fd, &stbuf);
    if (res == -1) {
    PERROR("fstat");
    return -1;
    }
    if ((stbuf.st_mode & 07777) != mode) {
    ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode);
    return -1;
    }
    return 0;
    }

    static int check_times(const char *path, time_t atime, time_t mtime)
    {
    int err = 0;
    struct stat stbuf;
    int res = lstat(path, &stbuf);
    if (res == -1) {
    PERROR("lstat");
    return -1;
    }
    if (stbuf.st_atime != atime) {
    ERROR("atime %li instead of %li", stbuf.st_atime, atime);
    err--;
    }
    if (stbuf.st_mtime != mtime) {
    ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
    err--;
    }
    if (err)
    return -1;

    return 0;
    }

    static int fcheck_times(int fd, time_t atime, time_t mtime)
    {
    int err = 0;
    struct stat stbuf;
    int res = fstat(fd, &stbuf);
    if (res == -1) {
    PERROR("fstat");
    return -1;
    }
    if (stbuf.st_atime != atime) {
    ERROR("atime %li instead of %li", stbuf.st_atime, atime);
    err--;
    }
    if (stbuf.st_mtime != mtime) {
    ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
    err--;
    }
    if (err)
    return -1;

    return 0;
    }

    static int check_nlink(const char *path, nlink_t nlink)
    {
    struct stat stbuf;
    int res = lstat(path, &stbuf);
    if (res == -1) {
    PERROR("lstat");
    return -1;
    }
    if (stbuf.st_nlink != nlink) {
    ERROR("nlink %li instead of %li", (long) stbuf.st_nlink, (long) nlink);
    return -1;
    }
    return 0;
    }

    static int fcheck_nlink(int fd, nlink_t nlink)
    {
    struct stat stbuf;
    int res = fstat(fd, &stbuf);
    if (res == -1) {
    PERROR("fstat");
    return -1;
    }
    if (stbuf.st_nlink != nlink) {
    ERROR("nlink %li instead of %li", (long) stbuf.st_nlink, (long) nlink);
    return -1;
    }
    return 0;
    }

    static int check_nonexist(const char *path)
    {
    struct stat stbuf;
    int res = lstat(path, &stbuf);
    if (res == 0) {
    ERROR("file should not exist");
    return -1;
    }
    if (errno != ENOENT) {
    ERROR("file should not exist: %s", strerror(errno));
    return -1;
    }
    return 0;
    }

    static int check_buffer(const char *buf, const char *data, unsigned len)
    {
    if (memcmp(buf, data, len) != 0) {
    ERROR("data mismatch");
    return -1;
    }
    return 0;
    }

    static int check_data(const char *path, const char *data, int offset,
    unsigned len)
    {
    char buf[4096];
    int res;
    int fd = open(path, O_RDONLY);
    if (fd == -1) {
    PERROR("open");
    return -1;
    }
    if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
    PERROR("lseek");
    close(fd);
    return -1;
    }
    while (len) {
    int rdlen = len < sizeof(buf) ? len : sizeof(buf);
    res = read(fd, buf, rdlen);
    if (res == -1) {
    PERROR("read");
    close(fd);
    return -1;
    }
    if (res != rdlen) {
    ERROR("short read: %u instead of %u", res, rdlen);
    close(fd);
    return -1;
    }
    if (check_buffer(buf, data, rdlen) != 0) {
    close(fd);
    return -1;
    }
    data += rdlen;
    len -= rdlen;
    }
    res = close(fd);
    if (res == -1) {
    PERROR("close");
    return -1;
    }
    return 0;
    }

    static int fcheck_data(int fd, const char *data, int offset,
    unsigned len)
    {
    char buf[4096];
    int res;
    if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
    PERROR("lseek");
    return -1;
    }
    while (len) {
    int rdlen = len < sizeof(buf) ? len : sizeof(buf);
    res = read(fd, buf, rdlen);
    if (res == -1) {
    PERROR("read");
    return -1;
    }
    if (res != rdlen) {
    ERROR("short read: %u instead of %u", res, rdlen);
    return -1;
    }
    if (check_buffer(buf, data, rdlen) != 0) {
    return -1;
    }
    data += rdlen;
    len -= rdlen;
    }
    return 0;
    }

    static int check_dir_contents(const char *path, const char **contents)
    {
    int i;
    int res;
    int err = 0;
    int found[MAX_ENTRIES];
    const char *cont[MAX_ENTRIES];
    DIR *dp;

    for (i = 0; contents[i]; i++) {
    assert(i < MAX_ENTRIES - 3);
    found[i] = 0;
    cont[i] = contents[i];
    }
    found[i] = 0;
    cont[i++] = ".";
    found[i] = 0;
    cont[i++] = "..";
    cont[i] = NULL;

    dp = opendir(path);
    if (dp == NULL) {
    PERROR("opendir");
    return -1;
    }
    memset(found, 0, sizeof(found));
    while(1) {
    struct dirent *de;
    errno = 0;
    de = readdir(dp);
    if (de == NULL) {
    if (errno) {
    PERROR("readdir");
    closedir(dp);
    return -1;
    }
    break;
    }
    for (i = 0; cont[i] != NULL; i++) {
    assert(i < MAX_ENTRIES);
    if (strcmp(cont[i], de->d_name) == 0) {
    if (found[i]) {
    ERROR("duplicate entry <%s>", de->d_name);
    err--;
    } else
    found[i] = 1;
    break;
    }
    }
    if (!cont[i]) {
    ERROR("unexpected entry <%s>", de->d_name);
    err --;
    }
    }
    for (i = 0; cont[i] != NULL; i++) {
    if (!found[i]) {
    ERROR("missing entry <%s>", cont[i]);
    err--;
    }
    }
    res = closedir(dp);
    if (res == -1) {
    PERROR("closedir");
    return -1;
    }
    if (err)
    return -1;

    return 0;
    }

    static int create_file(const char *path, const char *data, int len)
    {
    int res;
    int fd;

    unlink(path);
    fd = creat(path, 0644);
    if (fd == -1) {
    PERROR("creat");
    return -1;
    }
    if (len) {
    res = write(fd, data, len);
    if (res == -1) {
    PERROR("write");
    close(fd);
    return -1;
    }
    if (res != len) {
    ERROR("write is short: %u instead of %u", res, len);
    close(fd);
    return -1;
    }
    }
    res = close(fd);
    if (res == -1) {
    PERROR("close");
    return -1;
    }
    res = check_type(path, S_IFREG);
    if (res == -1)
    return -1;
    res = check_mode(path, 0644);
    if (res == -1)
    return -1;
    res = check_nlink(path, 1);
    if (res == -1)
    return -1;
    res = check_size(path, len);
    if (res == -1)
    return -1;

    if (len) {
    res = check_data(path, data, 0, len);
    if (res == -1)
    return -1;
    }

    return 0;
    }

    static int cleanup_dir(const char *path, const char **dir_files, int quiet)
    {
    int i;
    int err = 0;

    for (i = 0; dir_files[i]; i++) {
    int res;
    char fpath[1024];
    sprintf(fpath, "%s/%s", path, dir_files[i]);
    res = unlink(fpath);
    if (res == -1 && !quiet) {
    PERROR("unlink");
    err --;
    }
    }
    if (err)
    return -1;

    return 0;
    }

    static int create_dir(const char *path, const char **dir_files)
    {
    int res;
    int i;

    rmdir(path);
    res = mkdir(path, 0755);
    if (res == -1) {
    PERROR("mkdir");
    return -1;
    }
    res = check_type(path, S_IFDIR);
    if (res == -1)
    return -1;
    res = check_mode(path, 0755);
    if (res == -1)
    return -1;

    for (i = 0; dir_files[i]; i++) {
    char fpath[1024];
    sprintf(fpath, "%s/%s", path, dir_files[i]);
    res = create_file(fpath, "", 0);
    if (res == -1) {
    cleanup_dir(path, dir_files, 1);
    return -1;
    }
    }
    res = check_dir_contents(path, dir_files);
    if (res == -1) {
    cleanup_dir(path, dir_files, 1);
    return -1;
    }

    return 0;
    }

    static int test_truncate(int len)
    {
    const char *data = testdata;
    int datalen = testdatalen;
    int res;

    start_test("truncate(%u)", (int) len);
    res = create_file(testfile, data, datalen);
    if (res == -1)
    return -1;

    res = truncate(testfile, len);
    if (res == -1) {
    PERROR("truncate");
    return -1;
    }
    res = check_size(testfile, len);
    if (res == -1)
    return -1;

    if (len > 0) {
    if (len <= datalen) {
    res = check_data(testfile, data, 0, len);
    if (res == -1)
    return -1;
    } else {
    res = check_data(testfile, data, 0, datalen);
    if (res == -1)
    return -1;
    res = check_data(testfile, zerodata, datalen, len - datalen);
    if (res == -1)
    return -1;
    }
    }
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;

    success();
    return 0;
    }

    static int test_ftruncate(int len, int mode)
    {
    const char *data = testdata;
    int datalen = testdatalen;
    int res;
    int fd;

    start_test("ftruncate(%u) mode: 0%03o", len, mode);
    res = create_file(testfile, data, datalen);
    if (res == -1)
    return -1;

    fd = open(testfile, O_WRONLY);
    if (fd == -1) {
    PERROR("open");
    return -1;
    }

    res = fchmod(fd, mode);
    if (res == -1) {
    PERROR("fchmod");
    close(fd);
    return -1;
    }
    res = check_mode(testfile, mode);
    if (res == -1) {
    close(fd);
    return -1;
    }
    res = ftruncate(fd, len);
    if (res == -1) {
    PERROR("ftruncate");
    close(fd);
    return -1;
    }
    close(fd);
    res = check_size(testfile, len);
    if (res == -1)
    return -1;

    if (len > 0) {
    if (len <= datalen) {
    res = check_data(testfile, data, 0, len);
    if (res == -1)
    return -1;
    } else {
    res = check_data(testfile, data, 0, datalen);
    if (res == -1)
    return -1;
    res = check_data(testfile, zerodata, datalen, len - datalen);
    if (res == -1)
    return -1;
    }
    }
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;

    success();
    return 0;
    }

    static int test_utime(void)
    {
    struct utimbuf utm;
    time_t atime = 987631200;
    time_t mtime = 123116400;
    int res;

    start_test("utime");
    res = create_file(testfile, NULL, 0);
    if (res == -1)
    return -1;

    utm.actime = atime;
    utm.modtime = mtime;
    res = utime(testfile, &utm);
    if (res == -1) {
    PERROR("utime");
    return -1;
    }
    res = check_times(testfile, atime, mtime);
    if (res == -1) {
    return -1;
    }
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;

    success();
    return 0;
    }

    static int test_create(void)
    {
    const char *data = testdata;
    int datalen = testdatalen;
    int err = 0;
    int res;
    int fd;

    start_test("create");
    unlink(testfile);
    fd = creat(testfile, 0644);
    if (fd == -1) {
    PERROR("creat");
    return -1;
    }
    res = write(fd, data, datalen);
    if (res == -1) {
    PERROR("write");
    close(fd);
    return -1;
    }
    if (res != datalen) {
    ERROR("write is short: %u instead of %u", res, datalen);
    close(fd);
    return -1;
    }
    res = close(fd);
    if (res == -1) {
    PERROR("close");
    return -1;
    }
    res = check_type(testfile, S_IFREG);
    if (res == -1)
    return -1;
    err += check_mode(testfile, 0644);
    err += check_nlink(testfile, 1);
    err += check_size(testfile, datalen);
    err += check_data(testfile, data, 0, datalen);
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    static int test_create_unlink(void)
    {
    const char *data = testdata;
    int datalen = testdatalen;
    int err = 0;
    int res;
    int fd;

    start_test("create+unlink");
    unlink(testfile);
    fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd == -1) {
    PERROR("creat");
    return -1;
    }
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    close(fd);
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;
    res = write(fd, data, datalen);
    if (res == -1) {
    PERROR("write");
    close(fd);
    return -1;
    }
    if (res != datalen) {
    ERROR("write is short: %u instead of %u", res, datalen);
    close(fd);
    return -1;
    }
    err += fcheck_type(fd, S_IFREG);
    err += fcheck_mode(fd, 0644);
    err += fcheck_nlink(fd, 0);
    err += fcheck_size(fd, datalen);
    err += fcheck_data(fd, data, 0, datalen);
    res = close(fd);
    if (res == -1) {
    PERROR("close");
    err--;
    }
    if (err)
    return -1;

    success();
    return 0;
    }

    static int test_mknod(void)
    {
    int err = 0;
    int res;

    start_test("mknod");
    unlink(testfile);
    res = mknod(testfile, 0644, 0);
    if (res == -1) {
    PERROR("mknod");
    return -1;
    }
    res = check_type(testfile, S_IFREG);
    if (res == -1)
    return -1;
    err += check_mode(testfile, 0644);
    err += check_nlink(testfile, 1);
    err += check_size(testfile, 0);
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    #define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode)

    static int do_test_open(int exist, int flags, const char *flags_str, int mode)
    {
    char buf[4096];
    const char *data = testdata;
    int datalen = testdatalen;
    unsigned currlen = 0;
    int err = 0;
    int res;
    int fd;
    off_t off;

    start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
    unlink(testfile);
    if (exist) {
    res = create_file(testfile, testdata2, testdata2len);
    if (res == -1)
    return -1;

    currlen = testdata2len;
    }

    fd = open(testfile, flags, mode);
    if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
    if (fd != -1) {
    ERROR("open should have failed");
    close(fd);
    return -1;
    } else if (errno == EEXIST)
    goto succ;
    }
    if (!(flags & O_CREAT) && !exist) {
    if (fd != -1) {
    ERROR("open should have failed");
    close(fd);
    return -1;
    } else if (errno == ENOENT)
    goto succ;
    }
    if (fd == -1) {
    PERROR("open");
    return -1;
    }

    if (flags & O_TRUNC)
    currlen = 0;

    err += check_type(testfile, S_IFREG);
    if (exist)
    err += check_mode(testfile, 0644);
    else
    err += check_mode(testfile, mode);
    err += check_nlink(testfile, 1);
    err += check_size(testfile, currlen);
    if (exist && !(flags & O_TRUNC) && (mode & 0400))
    err += check_data(testfile, testdata2, 0, testdata2len);

    res = write(fd, data, datalen);
    if ((flags & O_ACCMODE) != O_RDONLY) {
    if (res == -1) {
    PERROR("write");
    err --;
    } else if (res != datalen) {
    ERROR("write is short: %u instead of %u", res, datalen);
    err --;
    } else {
    if (datalen > (int) currlen)
    currlen = datalen;

    err += check_size(testfile, currlen);

    if (mode & 0400) {
    err += check_data(testfile, data, 0, datalen);
    if (exist && !(flags & O_TRUNC) && testdata2len > datalen)
    err += check_data(testfile, testdata2 + datalen, datalen,
    testdata2len - datalen);
    }
    }
    } else {
    if (res != -1) {
    ERROR("write should have failed");
    err --;
    } else if (errno != EBADF) {
    PERROR("write");
    err --;
    }
    }
    off = lseek(fd, SEEK_SET, 0);
    if (off == (off_t) -1) {
    PERROR("lseek");
    err--;
    } else if (off != 0) {
    ERROR("offset should have returned 0");
    err --;
    }
    res = read(fd, buf, sizeof(buf));
    if ((flags & O_ACCMODE) != O_WRONLY) {
    if (res == -1) {
    PERROR("read");
    err--;
    } else {
    int readsize = currlen < sizeof(buf) ? currlen : sizeof(buf);
    if (res != readsize) {
    ERROR("read is short: %i instead of %u", res, readsize);
    err--;
    } else {
    if ((flags & O_ACCMODE) != O_RDONLY) {
    err += check_buffer(buf, data, datalen);
    if (exist && !(flags & O_TRUNC) && testdata2len > datalen)
    err += check_buffer(buf + datalen, testdata2 + datalen,
    testdata2len - datalen);
    } else if (exist)
    err += check_buffer(buf, testdata2, testdata2len);
    }
    }
    } else {
    if (res != -1) {
    ERROR("read should have failed");
    err --;
    } else if (errno != EBADF) {
    PERROR("read");
    err --;
    }
    }

    res = close(fd);
    if (res == -1) {
    PERROR("close");
    return -1;
    }
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    succ:
    success();
    return 0;
    }

    #define test_open_acc(flags, mode, err) do_test_open_acc(flags, #flags, mode, err)

    static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
    {
    const char *data = testdata;
    int datalen = testdatalen;
    int res;
    int fd;

    start_test("open_acc(%s) mode: 0%03o error: '%s'", flags_str, mode,
    strerror(err));
    unlink(testfile);
    res = create_file(testfile, data, datalen);
    if (res == -1)
    return -1;

    res = chmod(testfile, mode);
    if (res == -1) {
    PERROR("chmod");
    return -1;
    }

    res = check_mode(testfile, mode);
    if (res == -1)
    return -1;

    fd = open(testfile, flags);
    if (fd == -1) {
    if (err != errno) {
    PERROR("open");
    return -1;
    }
    } else {
    if (err) {
    ERROR("open should have failed");
    close(fd);
    return -1;
    }
    close(fd);
    }
    success();
    return 0;
    }

    static int test_symlink(void)
    {
    char buf[1024];
    const char *data = testdata;
    int datalen = testdatalen;
    int linklen = strlen(testfile);
    int err = 0;
    int res;

    start_test("symlink");
    res = create_file(testfile, data, datalen);
    if (res == -1)
    return -1;

    unlink(testfile2);
    res = symlink(testfile, testfile2);
    if (res == -1) {
    PERROR("symlink");
    return -1;
    }
    res = check_type(testfile2, S_IFLNK);
    if (res == -1)
    return -1;
    err += check_mode(testfile2, 0777);
    err += check_nlink(testfile2, 1);
    res = readlink(testfile2, buf, sizeof(buf));
    if (res == -1) {
    PERROR("readlink");
    err--;
    }
    if (res != linklen) {
    ERROR("short readlink: %u instead of %u", res, linklen);
    err--;
    }
    if (memcmp(buf, testfile, linklen) != 0) {
    ERROR("link mismatch");
    err--;
    }
    err += check_size(testfile2, datalen);
    err += check_data(testfile2, data, 0, datalen);
    res = unlink(testfile2);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile2);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    static int test_link(void)
    {
    const char *data = testdata;
    int datalen = testdatalen;
    int err = 0;
    int res;

    start_test("link");
    res = create_file(testfile, data, datalen);
    if (res == -1)
    return -1;

    unlink(testfile2);
    res = link(testfile, testfile2);
    if (res == -1) {
    PERROR("link");
    return -1;
    }
    res = check_type(testfile2, S_IFREG);
    if (res == -1)
    return -1;
    err += check_mode(testfile2, 0644);
    err += check_nlink(testfile2, 2);
    err += check_size(testfile2, datalen);
    err += check_data(testfile2, data, 0, datalen);
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;

    err += check_nlink(testfile2, 1);
    res = unlink(testfile2);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile2);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    static int test_rename_file(void)
    {
    const char *data = testdata;
    int datalen = testdatalen;
    int err = 0;
    int res;

    start_test("rename file");
    res = create_file(testfile, data, datalen);
    if (res == -1)
    return -1;

    unlink(testfile2);
    res = rename(testfile, testfile2);
    if (res == -1) {
    PERROR("rename");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;
    res = check_type(testfile2, S_IFREG);
    if (res == -1)
    return -1;
    err += check_mode(testfile2, 0644);
    err += check_nlink(testfile2, 1);
    err += check_size(testfile2, datalen);
    err += check_data(testfile2, data, 0, datalen);
    res = unlink(testfile2);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile2);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    static int test_rename_dir(void)
    {
    int err = 0;
    int res;

    start_test("rename dir");
    res = create_dir(testdir, testdir_files);
    if (res == -1)
    return -1;

    rmdir(testdir2);
    res = rename(testdir, testdir2);
    if (res == -1) {
    PERROR("rename");
    cleanup_dir(testdir, testdir_files, 1);
    return -1;
    }
    res = check_nonexist(testdir);
    if (res == -1) {
    cleanup_dir(testdir, testdir_files, 1);
    return -1;
    }
    res = check_type(testdir2, S_IFDIR);
    if (res == -1) {
    cleanup_dir(testdir2, testdir_files, 1);
    return -1;
    }
    err += check_mode(testdir2, 0755);
    err += check_dir_contents(testdir2, testdir_files);
    err += cleanup_dir(testdir2, testdir_files, 0);
    res = rmdir(testdir2);
    if (res == -1) {
    PERROR("rmdir");
    return -1;
    }
    res = check_nonexist(testdir2);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    static int test_mkfifo(void)
    {
    int res;
    int err = 0;

    start_test("mkfifo");
    unlink(testfile);
    res = mkfifo(testfile, 0644);
    if (res == -1) {
    PERROR("mkfifo");
    return -1;
    }
    res = check_type(testfile, S_IFIFO);
    if (res == -1)
    return -1;
    err += check_mode(testfile, 0644);
    err += check_nlink(testfile, 1);
    res = unlink(testfile);
    if (res == -1) {
    PERROR("unlink");
    return -1;
    }
    res = check_nonexist(testfile);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    static int test_mkdir(void)
    {
    int res;
    int err = 0;
    const char *dir_contents[] = {NULL};

    start_test("mkdir");
    rmdir(testdir);
    res = mkdir(testdir, 0755);
    if (res == -1) {
    PERROR("mkdir");
    return -1;
    }
    res = check_type(testdir, S_IFDIR);
    if (res == -1)
    return -1;
    err += check_mode(testdir, 0755);
    err += check_nlink(testdir, 2);
    err += check_dir_contents(testdir, dir_contents);
    res = rmdir(testdir);
    if (res == -1) {
    PERROR("rmdir");
    return -1;
    }
    res = check_nonexist(testdir);
    if (res == -1)
    return -1;
    if (err)
    return -1;

    success();
    return 0;
    }

    int main(int argc, char *argv[])
    {
    const char *basepath;
    int err = 0;

    umask(0);
    if (argc != 2) {
    fprintf(stderr, "usage: %s testdir\n", argv[0]);
    return 1;
    }
    basepath = argv[1];
    assert(strlen(basepath) < 512);

    sprintf(testfile, "%s/testfile", basepath);
    sprintf(testfile2, "%s/testfile2", basepath);
    sprintf(testdir, "%s/testdir", basepath);
    sprintf(testdir2, "%s/testdir2", basepath);
    err += test_create();
    err += test_create_unlink();
    err += test_mknod();
    err += test_symlink();
    err += test_link();
    err += test_mkfifo();
    err += test_mkdir();
    err += test_rename_file();
    err += test_rename_dir();
    err += test_utime();
    err += test_truncate(0);
    err += test_truncate(testdatalen / 2);
    err += test_truncate(testdatalen);
    err += test_truncate(testdatalen + 100);
    err += test_ftruncate(0, 0600);
    err += test_ftruncate(testdatalen / 2, 0600);
    err += test_ftruncate(testdatalen, 0600);
    err += test_ftruncate(testdatalen + 100, 0600);
    err += test_ftruncate(0, 0400);
    err += test_ftruncate(0, 0200);
    err += test_ftruncate(0, 0000);
    err += test_open(0, O_RDONLY, 0);
    err += test_open(1, O_RDONLY, 0);
    err += test_open(1, O_RDWR, 0);
    err += test_open(1, O_WRONLY, 0);
    err += test_open(0, O_RDWR | O_CREAT, 0600);
    err += test_open(1, O_RDWR | O_CREAT, 0600);
    err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
    err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
    err += test_open(0, O_RDONLY | O_CREAT, 0600);
    err += test_open(0, O_RDONLY | O_CREAT, 0400);
    err += test_open(0, O_RDONLY | O_CREAT, 0200);
    err += test_open(0, O_RDONLY | O_CREAT, 0000);
    err += test_open(0, O_WRONLY | O_CREAT, 0600);
    err += test_open(0, O_WRONLY | O_CREAT, 0400);
    err += test_open(0, O_WRONLY | O_CREAT, 0200);
    err += test_open(0, O_WRONLY | O_CREAT, 0000);
    err += test_open(0, O_RDWR | O_CREAT, 0400);
    err += test_open(0, O_RDWR | O_CREAT, 0200);
    err += test_open(0, O_RDWR | O_CREAT, 0000);
    err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
    err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
    err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
    err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
    err += test_open_acc(O_RDONLY, 0600, 0);
    err += test_open_acc(O_WRONLY, 0600, 0);
    err += test_open_acc(O_RDWR, 0600, 0);
    err += test_open_acc(O_RDONLY, 0400, 0);
    err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
    err += test_open_acc(O_WRONLY, 0400, EACCES);
    err += test_open_acc(O_RDWR, 0400, EACCES);
    err += test_open_acc(O_RDONLY, 0200, EACCES);
    err += test_open_acc(O_WRONLY, 0200, 0);
    err += test_open_acc(O_RDWR, 0200, EACCES);
    err += test_open_acc(O_RDONLY, 0000, EACCES);
    err += test_open_acc(O_WRONLY, 0000, EACCES);
    err += test_open_acc(O_RDWR, 0000, EACCES);

    unlink(testfile);
    unlink(testfile2);
    rmdir(testdir);
    rmdir(testdir2);

    if (err) {
    fprintf(stderr, "%i tests failed\n", -err);
    return 1;
    }

    return 0;
    }
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  4. Re: [patch] fix sys_mknodat breakage from r/o bind mounts

    On Wed, Sep 26, 2007 at 12:58:47AM +0200, Miklos Szeredi wrote:
    > if (!S_ISREG(mode) && !S_ISCHR(mode) && !S_ISBLK(mode) &&
    > - !S_ISFIFO(mode) && !S_ISSOCK(mode) && mode != 0) {
    > + !S_ISFIFO(mode) && !S_ISSOCK(mode) && (mode & S_IFMT) != 0) {


    FYI this whole section might be cleaner as


    static int may_mknod(mode_t mode)
    {
    switch (mode & S_IFMT) {
    case S_IFREG:
    case S_IFCHR:
    case S_IFBLK:
    case S_IFFIFO:
    case S_IFSOCK:
    case 0: /* zero mode translates to S_IFREG *
    return 0;
    case S_IFDIR:
    return -EPERM;
    default:
    return -EINVAL;
    }
    }

    and then in sys_mknodat just a

    error = may_mknod(mode);
    if (error)
    goto out_dput;

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

+ Reply to Thread