diff options
author | Colin Walters <walters@verbum.org> | 2016-08-16 09:26:16 -0400 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2016-08-16 21:22:28 +0000 |
commit | 77af6844d8330b31d58080076afb31e08974ce09 (patch) | |
tree | c7db0b6e19a0c3f3923eca7189ea9138f4cac4d1 | |
parent | 3469161e4157d715285395e80202c9d2dbd43ce8 (diff) | |
download | ostree-77af6844d8330b31d58080076afb31e08974ce09.tar.gz |
rofiles-fuse: Rework to be based on nlink
Programs like `useradd` try to `open(/etc/passwd, O_RDWR)` to append,
which didn't work with rofiles-fuse. Thinking about this, I realized
that there's a simpler algorithm for "can we write to this file" which
is "does it have a hardlink count <= 1"?
Switching to this both drops complexity (we no longer need to keep a
hash table of files we created), and also lets useradd work.
Closes: #462
Approved by: jlebon
-rw-r--r-- | src/rofiles-fuse/main.c | 131 |
1 files changed, 43 insertions, 88 deletions
diff --git a/src/rofiles-fuse/main.c b/src/rofiles-fuse/main.c index 8468276d..ac44a438 100644 --- a/src/rofiles-fuse/main.c +++ b/src/rofiles-fuse/main.c @@ -39,7 +39,6 @@ // Global to store our read-write path static int basefd = -1; -static GHashTable *created_devino_hash = NULL; static inline const char * ENSURE_RELPATH (const char *path) @@ -50,51 +49,6 @@ ENSURE_RELPATH (const char *path) return path; } -typedef struct { - dev_t dev; - ino_t ino; -} DevIno; - -static guint -devino_hash (gconstpointer a) -{ - DevIno *a_i = (gpointer)a; - return (guint) (a_i->dev + a_i->ino); -} - -static int -devino_equal (gconstpointer a, - gconstpointer b) -{ - DevIno *a_i = (gpointer)a; - DevIno *b_i = (gpointer)b; - return a_i->dev == b_i->dev - && a_i->ino == b_i->ino; -} - -static gboolean -devino_set_contains (dev_t dev, ino_t ino) -{ - DevIno devino = { dev, ino }; - return g_hash_table_contains (created_devino_hash, &devino); -} - -static gboolean -devino_set_insert (dev_t dev, ino_t ino) -{ - DevIno *devino = g_new (DevIno, 1); - devino->dev = dev; - devino->ino = ino; - return g_hash_table_add (created_devino_hash, devino); -} - -static gboolean -devino_set_remove (dev_t dev, ino_t ino) -{ - DevIno devino = { dev, ino }; - return g_hash_table_remove (created_devino_hash, &devino); -} - static int callback_getattr (const char *path, struct stat *st_data) { @@ -188,15 +142,7 @@ callback_mkdir (const char *path, mode_t mode) static int callback_unlink (const char *path) { - struct stat stbuf; path = ENSURE_RELPATH (path); - - if (fstatat (basefd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0) - { - if (!S_ISDIR (stbuf.st_mode)) - devino_set_remove (stbuf.st_dev, stbuf.st_ino); - } - if (unlinkat (basefd, path, 0) == -1) return -errno; return 0; @@ -250,6 +196,12 @@ callback_link (const char *from, const char *to) return 0; } +static gboolean +stbuf_is_regfile_hardlinked (struct stat *stbuf) +{ + return S_ISREG (stbuf->st_mode) && stbuf->st_nlink > 1; +} + static int can_write (const char *path) { @@ -261,11 +213,8 @@ can_write (const char *path) else return -errno; } - if (!S_ISDIR (stbuf.st_mode)) - { - if (!devino_set_contains (stbuf.st_dev, stbuf.st_ino)) - return -EROFS; - } + if (stbuf_is_regfile_hardlinked (&stbuf)) + return -EROFS; return 0; } @@ -334,42 +283,50 @@ callback_utime (const char *path, struct utimbuf *buf) static int do_open (const char *path, mode_t mode, struct fuse_file_info *finfo) { - const int flags = finfo->flags & O_ACCMODE; int fd; struct stat stbuf; - /* Support read only opens */ - G_STATIC_ASSERT (O_RDONLY == 0); - path = ENSURE_RELPATH (path); - if (flags == 0) - fd = openat (basefd, path, flags); + if ((finfo->flags & O_ACCMODE) == O_RDONLY) + { + /* Read */ + fd = openat (basefd, path, finfo->flags); + if (fd == -1) + return -errno; + } else { - const int forced_excl_flags = flags | O_CREAT | O_EXCL; - /* Do an exclusive open, don't allow writable fds for existing - files */ - fd = openat (basefd, path, forced_excl_flags, mode); - /* If they didn't specify O_EXCL, give them EROFS if the file - * exists. - */ - if (fd == -1 && (flags & O_EXCL) == 0) - { - if (errno == EEXIST) - errno = EROFS; - } - else if (fd != -1) - { - if (fstat (fd, &stbuf) == -1) - return -errno; - devino_set_insert (stbuf.st_dev, stbuf.st_ino); - } + /* Write */ + + /* We need to specially handle O_TRUNC */ + fd = openat (basefd, path, finfo->flags & ~O_TRUNC, mode); + if (fd == -1) + return -errno; + + if (fstat (fd, &stbuf) == -1) + { + (void) close (fd); + return -errno; + } + + if (stbuf_is_regfile_hardlinked (&stbuf)) + { + (void) close (fd); + return -EROFS; + } + + /* Handle O_TRUNC here only after verifying hardlink state */ + if (finfo->flags & O_TRUNC) + { + if (ftruncate (fd, 0) == -1) + { + (void) close (fd); + return -errno; + } + } } - if (fd == -1) - return -errno; - finfo->fh = fd; return 0; @@ -594,8 +551,6 @@ main (int argc, char *argv[]) exit (EXIT_FAILURE); } - created_devino_hash = g_hash_table_new_full (devino_hash, devino_equal, g_free, NULL); - fuse_main (args.argc, args.argv, &callback_oper, NULL); return 0; |