Browse Source

[LibOS] Add more cases to update inode number and file size

This commit adds more cases to the chroot FS to update file metadata:
- update inode number immediately after creating a new file;
- update file size after writing a non-mmapped file.

This commit also adds corresponding LibOS tests.
jack.wxz 4 years ago
parent
commit
c6b9ea1baf

+ 23 - 9
LibOS/shim/src/fs/chroot/fs.c

@@ -417,6 +417,11 @@ static int __chroot_open (struct shim_dentry * dent,
             if (!palhdl)
                 return -PAL_ERRNO;
         }
+
+        /* If DENTRY_LISTED is set on the parent dentry, list_directory_dentry() will not update
+         * dent's ino, so ino will be actively updated here. */
+        if (creat)
+            chroot_update_ino(dent);
     }
 
     if (!data->queried) {
@@ -576,6 +581,19 @@ static inline bool check_version (struct shim_handle * hdl)
            == hdl->info.file.version;
 }
 
+static void chroot_update_size(struct shim_handle* hdl, struct shim_file_handle* file,
+                               struct shim_file_data* data) {
+    if (check_version(hdl)) {
+        off_t size;
+        do {
+            if ((size = atomic_read(&data->size)) >= file->size) {
+                file->size = size;
+                break;
+            }
+        } while ((off_t)atomic_cmpxchg(&data->size, size, file->size) != size);
+    }
+}
+
 static int chroot_hstat (struct shim_handle * hdl, struct stat * stat)
 {
     int ret;
@@ -763,15 +781,7 @@ static ssize_t map_write (struct shim_handle * hdl, const void * buf, size_t cou
            file->size -= count - pal_ret;
         }
 
-        if (check_version(hdl)) {
-            off_t size;
-            do {
-                if ((size = atomic_read(&data->size)) >= file->size) {
-                    file->size = size;
-                    break;
-                }
-            } while ((off_t) atomic_cmpxchg(&data->size, size, file->size) != size);
-        }
+        chroot_update_size(hdl, file, data);
 
         if (__builtin_add_overflow(marker, pal_ret, &file->marker)) {
             // Should never happen. Even if it would, we couldn't recover from this condition.
@@ -887,6 +897,10 @@ static ssize_t chroot_write (struct shim_handle * hdl, const void * buf, size_t
             BUG();
         if (file->type != FILE_TTY && __builtin_add_overflow(file->marker, pal_ret, &file->marker))
             BUG();
+        if (file->marker > file->size) {
+            file->size = file->marker;
+            chroot_update_size(hdl, file, FILE_HANDLE_DATA(hdl));
+        }
     } else {
         ret = PAL_NATIVE_ERRNO == PAL_ERROR_ENDOFSTREAM ?  0 : -PAL_ERRNO;
     }

+ 2 - 0
LibOS/shim/src/fs/shim_namei.c

@@ -780,6 +780,8 @@ int list_directory_dentry (struct shim_dentry *dent) {
         child->ino = d->ino;
     }
 
+    /* Once DENTRY_LISTED is set, the ino of the newly created file will not be updated, so its
+     * ino needs to be set in create() or open(O_CREAT). */
     dent->state |= DENTRY_LISTED;
     ret = 0;
 

+ 142 - 0
LibOS/shim/test/regression/file_size.c

@@ -0,0 +1,142 @@
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+#define BUF_LENGTH  4096
+#define HOLE_LENGTH 8192
+
+#define TEST_DIR  "tmp"
+#define TEST_FILE "__testfile__"
+
+ssize_t rw_file(int fd, char* buf, size_t bytes, bool write_flag) {
+    ssize_t rv = 0;
+    ssize_t ret;
+
+    while (bytes > rv) {
+        if (write_flag)
+            ret = write(fd, buf + rv, bytes - rv);
+        else
+            ret = read(fd, buf + rv, bytes - rv);
+
+        if (ret > 0) {
+            rv += ret;
+        } else {
+            if (ret < 0 && (errno == EAGAIN || errno == EINTR)) {
+                continue;
+            } else {
+                fprintf(stderr, "%s failed:%s\n", write_flag ? "write" : "read", strerror(errno));
+                return ret;
+            }
+        }
+    }
+
+    return rv;
+}
+
+int main(int argc, const char** argv) {
+    char buf[BUF_LENGTH];
+    ssize_t bytes;
+    int fd = 0;
+    int rv = 0;
+
+    memset(buf, 0, BUF_LENGTH);
+
+    /* setup the test directory and file */
+    rv = mkdir(TEST_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+    if (rv < 0 && errno != EEXIST) {
+        perror("mkdir failed");
+        return 1;
+    }
+
+    rv = unlink(TEST_DIR"/"TEST_FILE);
+    if (rv < 0 && errno != ENOENT) {
+        perror("unlink failed");
+        return 1;
+    }
+
+    fd = open(TEST_DIR"/"TEST_FILE, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+    if (fd < 0) {
+        perror("open failed");
+        return 1;
+    }
+
+    /* test file size: write a file of type != FILEBUF_MAP */
+    bytes = rw_file(fd, buf, BUF_LENGTH, true);
+    if (bytes != BUF_LENGTH) {
+        perror("writing " STR(BUF_LENGTH) " bytes to test file failed");
+        return 1;
+    }
+
+    rv = lseek(fd, 0, SEEK_SET);
+    if (rv < 0) {
+        perror("lseek to beginning of file failed");
+        return 1;
+    }
+
+    bytes = rw_file(fd, buf, 1, true);
+    if (bytes != 1) {
+        perror("writing one byte to test file failed");
+        return 1;
+    }
+
+    rv = lseek(fd, HOLE_LENGTH, SEEK_SET);
+    if (rv < 0) {
+        perror("lseek to " STR(HOLE_LENGTH) " offset failed");
+        return 1;
+    }
+
+    bytes = rw_file(fd, buf, BUF_LENGTH, true);
+    if (bytes != BUF_LENGTH) {
+        perror("writing " STR(BUF_LENGTH) " bytes to test file failed");
+        return 1;
+    }
+
+    rv = close(fd);
+    if (rv < 0) {
+        perror("close failed");
+        return 1;
+    }
+
+    fd = open(TEST_DIR"/"TEST_FILE, O_RDONLY);
+    if (fd < 0) {
+        perror("open failed");
+        return 1;
+    }
+
+    /* reopen file: file size should be HOLE_LENGTH + BUF_LENGTH */
+    rv = lseek(fd, HOLE_LENGTH, SEEK_SET);
+    if (rv < 0) {
+        perror("lseek to " STR(HOLE_LENGTH) " offset failed");
+        return 1;
+    }
+
+    bytes = rw_file(fd, buf, BUF_LENGTH, false);
+    if (bytes != BUF_LENGTH) {
+        perror("reading " STR(BUF_LENGTH) " bytes from test file failed");
+        return 1;
+    }
+
+    rv = close(fd);
+    if (rv < 0) {
+        perror("close failed");
+        return 1;
+    }
+
+    rv = unlink(TEST_DIR"/"TEST_FILE);
+    if (rv < 0) {
+        perror("unlink failed");
+        return 1;
+    }
+
+    printf("test completed successfully\n");
+    return 0;
+}

+ 93 - 0
LibOS/shim/test/regression/readdir.c

@@ -0,0 +1,93 @@
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define TEST_DIR  "tmp"
+#define TEST_FILE "__testfile__"
+
+/* return true if file_name exists, otherwise false */
+bool find_file(char* dir_name, char* file_name) {
+    bool found = false;
+    DIR* dir = opendir(dir_name);
+    if (dir == NULL) {
+        perror("opendir failed");
+        return found;
+    }
+
+    struct dirent* dent = NULL;
+    while (1) {
+        errno = 0;
+        dent = readdir(dir);
+        if (dent == NULL) {
+            if (errno == 0)
+                break;
+            perror("readdir failed");
+            goto out;
+        }
+
+        if (strncmp(file_name, dent->d_name, strlen(file_name)) == 0) {
+            found = true;
+            break;
+        }
+    }
+
+out:
+    closedir(dir);
+    return found;
+}
+
+int main(int argc, const char** argv) {
+    int rv = 0;
+    int fd = 0;
+
+    /* setup the test directory and file */
+    rv = mkdir(TEST_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+    if (rv < 0 && errno != EEXIST) {
+        perror("mkdir failed");
+        return 1;
+    }
+
+    rv = unlink(TEST_DIR"/"TEST_FILE);
+    if (rv < 0 && errno != ENOENT) {
+        perror("unlink failed");
+        return 1;
+    }
+
+    /* test readdir: should not find a file that we just deleted */
+    if (find_file(TEST_DIR, TEST_FILE)) {
+        perror("file " TEST_FILE " was unexpectedly found\n");
+        return 1;
+    }
+
+    fd = open(TEST_DIR"/"TEST_FILE, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+    if (fd < 0) {
+        perror("open failed");
+        return 1;
+    }
+
+    if (!find_file(TEST_DIR, TEST_FILE)) {
+        perror("file " TEST_FILE " was not found\n");
+        return 1;
+    }
+
+    rv = close(fd);
+    if (rv < 0) {
+        perror("close failed");
+        return 1;
+    }
+
+    rv = unlink(TEST_DIR"/"TEST_FILE);
+    if (rv < 0) {
+        perror("unlink failed");
+        return 1;
+    }
+
+    printf("test completed successfully\n");
+    return rv;
+}

+ 8 - 0
LibOS/shim/test/regression/test_libos.py

@@ -263,6 +263,14 @@ class TC_30_Syscall(RegressionTestCase):
         # fopen corner cases
         self.assertIn('Successfully read from file: Hello World', stdout)
 
+    def test_031_readdir(self):
+        stdout, _ = self.run_binary(['readdir'])
+        self.assertIn('test completed successfully', stdout)
+
+    def test_032_file_size(self):
+        stdout, _ = self.run_binary(['file_size'])
+        self.assertIn('test completed successfully', stdout)
+
     def test_040_futex_bitset(self):
         stdout, _ = self.run_binary(['futex_bitset'])