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
This commit is contained in:
parent
3469161e41
commit
77af6844d8
|
|
@ -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,41 +283,49 @@ 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 */
|
||||
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finfo->fh = fd;
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue