summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2016-08-16 09:26:16 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2016-08-16 21:22:28 +0000
commit77af6844d8330b31d58080076afb31e08974ce09 (patch)
treec7db0b6e19a0c3f3923eca7189ea9138f4cac4d1
parent3469161e4157d715285395e80202c9d2dbd43ce8 (diff)
downloadostree-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.c131
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;