/* Nautilus loves to use access(2) to test everything about a file, * such as whether it's executable. Therefore treat this a lot like * mount_local_getattr. */ static int mount_local_access (const char *path, int mask) { struct stat statbuf; int r; struct fuse_context *fuse; int ok = 1; DECL_G (); DEBUG_CALL ("%s, %d", path, mask); if (g->ml_read_only && (mask & W_OK)) return -EROFS; r = mount_local_getattr (path, &statbuf); if (r < 0 || mask == F_OK) { debug (g, "%s: mount_local_getattr returned r = %d", path, r); return r; } fuse = fuse_get_context (); /* Root user should be able to access everything, so only bother * with these fine-grained tests for non-root. (RHBZ#1106548). */ if (fuse->uid != 0) { if (mask & R_OK) ok = ok && ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IRUSR : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IRGRP : statbuf.st_mode & S_IROTH); if (mask & W_OK) ok = ok && ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IWUSR : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IWGRP : statbuf.st_mode & S_IWOTH); if (mask & X_OK) ok = ok && ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IXUSR : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IXGRP : statbuf.st_mode & S_IXOTH); } debug (g, "%s: " "testing access mask%s%s%s%s: " "caller UID:GID = %ju:%ju, " "file UID:GID = %ju:%ju, " "file mode = %o, " "result = %s", path, mask & R_OK ? " R_OK" : "", mask & W_OK ? " W_OK" : "", mask & X_OK ? " X_OK" : "", mask == 0 ? " 0" : "", (uintmax_t) fuse->uid, (uintmax_t) fuse->gid, (uintmax_t) statbuf.st_uid, (uintmax_t) statbuf.st_gid, statbuf.st_mode, ok ? "OK" : "EACCESS"); return ok ? 0 : -EACCES; }
static int mount_local_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { char *r; size_t rsize; const size_t limit = 2 * 1024 * 1024; DECL_G (); DEBUG_CALL ("%s, %p, %zu, %ld", path, buf, size, (long) offset); /* The guestfs protocol limits size to somewhere over 2MB. We just * reduce the requested size here accordingly and push the problem * up to every user. http://www.jwz.org/doc/worse-is-better.html */ if (size > limit) size = limit; r = guestfs_pread (g, path, size, offset, &rsize); if (r == NULL) RETURN_ERRNO; /* This should never happen, but at least it stops us overflowing * the output buffer if it does happen. */ if (rsize > size) rsize = size; memcpy (buf, r, rsize); free (r); return rsize; }
static int mount_local_readlink (const char *path, char *buf, size_t size) { const char *r; int free_it = 0; size_t len; DECL_G (); DEBUG_CALL ("%s, %p, %zu", path, buf, size); r = rlc_lookup (g, path); if (!r) { r = guestfs_readlink (g, path); if (r == NULL) RETURN_ERRNO; free_it = 1; } /* Note this is different from the real readlink(2) syscall. FUSE wants * the string to be always nul-terminated, even if truncated. */ len = strlen (r); if (len > size - 1) len = size - 1; memcpy (buf, r, len); buf[len] = '\0'; if (free_it) { char *tmp = (char *) r; free (tmp); } return 0; }
static int mount_local_statfs (const char *path, struct statvfs *stbuf) { CLEANUP_FREE_STATVFS struct guestfs_statvfs *r; DECL_G (); DEBUG_CALL ("%s, %p", path, stbuf); r = guestfs_statvfs (g, path); if (r == NULL) RETURN_ERRNO; stbuf->f_bsize = r->bsize; stbuf->f_frsize = r->frsize; stbuf->f_blocks = r->blocks; stbuf->f_bfree = r->bfree; stbuf->f_bavail = r->bavail; stbuf->f_files = r->files; stbuf->f_ffree = r->ffree; stbuf->f_favail = r->favail; stbuf->f_fsid = r->fsid; stbuf->f_flag = r->flag; stbuf->f_namemax = r->namemax; return 0; }
/* Ditto as above. */ static int mount_local_listxattr (const char *path, char *list, size_t size) { DECL_G (); DEBUG_CALL ("%s, %p, %zu", path, list, size); const struct guestfs_xattr_list *xattrs; int free_attrs = 0; xattrs = xac_lookup (g, path); if (xattrs == NULL) { xattrs = guestfs_lgetxattrs (g, path); if (xattrs == NULL) RETURN_ERRNO; free_attrs = 1; } /* Calculate how much space is required to hold the result. */ size_t space = 0; size_t len; size_t i; for (i = 0; i < xattrs->len; ++i) { len = strlen (xattrs->val[i].attrname) + 1; space += len; } /* The listxattr man page is unclear, but if list == NULL then we * return the space required (the caller then makes a second syscall * after allocating the required amount of space). If list != NULL * then it's not clear what we should do, but it appears we should * copy as much as possible and return -ERANGE if there's not enough * space in the buffer. */ ssize_t r; if (list == NULL) { r = space; goto out; } r = 0; for (i = 0; i < xattrs->len; ++i) { len = strlen (xattrs->val[i].attrname) + 1; if (size >= len) { memcpy (list, xattrs->val[i].attrname, len); size -= len; list += len; r += len; } else { r = -ERANGE; break; } } out: if (free_attrs) guestfs_free_xattr_list ((struct guestfs_xattr_list *) xattrs); return r; }
/* The guestfs(3) API for getting xattrs is much easier to use * than the real syscall. Unfortunately we now have to emulate * the real syscall using that API :-( */ static int mount_local_getxattr (const char *path, const char *name, char *value, size_t size) { DECL_G (); DEBUG_CALL ("%s, %s, %p, %zu", path, name, value, size); const struct guestfs_xattr_list *xattrs; int free_attrs = 0; xattrs = xac_lookup (g, path); if (xattrs == NULL) { xattrs = guestfs_lgetxattrs (g, path); if (xattrs == NULL) RETURN_ERRNO; free_attrs = 1; } /* Find the matching attribute (index in 'i'). */ ssize_t r; size_t i; for (i = 0; i < xattrs->len; ++i) { if (STREQ (xattrs->val[i].attrname, name)) break; } if (i == xattrs->len) { /* not found */ r = -ENOATTR; goto out; } /* The getxattr man page is unclear, but if value == NULL then we * return the space required (the caller then makes a second syscall * after allocating the required amount of space). If value != NULL * then it's not clear what we should do, but it appears we should * copy as much as possible and return -ERANGE if there's not enough * space in the buffer. */ size_t sz = xattrs->val[i].attrval_len; if (value == NULL) { r = sz; goto out; } if (sz <= size) r = sz; else { r = -ERANGE; sz = size; } memcpy (value, xattrs->val[i].attrval, sz); out: if (free_attrs) guestfs_free_xattr_list ((struct guestfs_xattr_list *) xattrs); return r; }
static int mount_local_release (const char *path, struct fuse_file_info *fi) { DECL_G (); DEBUG_CALL ("%s", path); /* Just a stub. This method is optional and can safely be left * unimplemented. */ return 0; }
/* All this function needs to do is to check that the requested open * flags are valid. See the notes in <fuse/fuse.h>. */ static int mount_local_open (const char *path, struct fuse_file_info *fi) { const int flags = fi->flags & O_ACCMODE; DECL_G (); DEBUG_CALL ("%s, 0%o", path, (unsigned) fi->flags); if (g->ml_read_only && flags != O_RDONLY) return -EROFS; return 0; }
static int mount_local_flush(const char *path, struct fuse_file_info *fi) { DECL_G (); DEBUG_CALL ("%s", path); /* Just a stub. This method is called whenever FUSE wants to flush the * pending changes (f.ex. to attributes) to a file. Since we don't have * anything to do and don't want FUSE to think something went badly, * just return 0. */ return 0; }
/* Emulate this by calling sync. */ static int mount_local_fsync (const char *path, int isdatasync, struct fuse_file_info *fi) { int r; DECL_G (); DEBUG_CALL ("%s, %d", path, isdatasync); r = guestfs_sync (g); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_truncate (const char *path, off_t size) { int r; DECL_G (); DEBUG_CALL ("%s, %ld", path, (long) size); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); r = guestfs_truncate_size (g, path, size); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_chown (const char *path, uid_t uid, gid_t gid) { int r; DECL_G (); DEBUG_CALL ("%s, %ju, %ju", path, (uintmax_t) uid, (uintmax_t) gid); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); r = guestfs_lchown (g, uid, gid, path); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_symlink (const char *from, const char *to) { int r; DECL_G (); DEBUG_CALL ("%s, %s", from, to); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, to); r = guestfs_ln_s (g, from, to); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_unlink (const char *path) { int r; DECL_G (); DEBUG_CALL ("%s", path); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); r = guestfs_rm (g, path); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_mknod (const char *path, mode_t mode, dev_t rdev) { int r; DECL_G (); DEBUG_CALL ("%s, 0%o, 0x%jx", path, mode, (uintmax_t) rdev); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); r = guestfs_mknod (g, mode, major (rdev), minor (rdev), path); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_chmod (const char *path, mode_t mode) { int r; DECL_G (); DEBUG_CALL ("%s, 0%o", path, mode); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); r = guestfs_chmod (g, mode, path); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_removexattr(const char *path, const char *name) { int r; DECL_G (); DEBUG_CALL ("%s, %s", path, name); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); r = guestfs_lremovexattr (g, name, path); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_getattr (const char *path, struct stat *statbuf) { const struct stat *buf; CLEANUP_FREE_STAT struct guestfs_statns *r = NULL; DECL_G (); DEBUG_CALL ("%s, %p", path, statbuf); buf = lsc_lookup (g, path); if (buf) { memcpy (statbuf, buf, sizeof *statbuf); return 0; } r = guestfs_lstatns (g, path); if (r == NULL) RETURN_ERRNO; memset (statbuf, 0, sizeof *statbuf); statbuf->st_dev = r->st_dev; statbuf->st_ino = r->st_ino; statbuf->st_mode = r->st_mode; statbuf->st_nlink = r->st_nlink; statbuf->st_uid = r->st_uid; statbuf->st_gid = r->st_gid; statbuf->st_rdev = r->st_rdev; statbuf->st_size = r->st_size; statbuf->st_blksize = r->st_blksize; statbuf->st_blocks = r->st_blocks; statbuf->st_atime = r->st_atime_sec; #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC statbuf->st_atim.tv_nsec = r->st_atime_nsec; #endif statbuf->st_mtime = r->st_mtime_sec; #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC statbuf->st_mtim.tv_nsec = r->st_mtime_nsec; #endif statbuf->st_ctime = r->st_ctime_sec; #ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC statbuf->st_ctim.tv_nsec = r->st_ctime_nsec; #endif return 0; }
static int mount_local_setxattr (const char *path, const char *name, const char *value, size_t size, int flags) { int r; DECL_G (); DEBUG_CALL ("%s, %s, %p, %zu", path, name, value, size); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); /* XXX Underlying guestfs(3) API doesn't understand the flags. */ r = guestfs_lsetxattr (g, name, value, size, path); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_utimens (const char *path, const struct timespec ts[2]) { int r; time_t atsecs, mtsecs; long atnsecs, mtnsecs; DECL_G (); DEBUG_CALL ("%s, [{ %ld, %ld }, { %ld, %ld }]", path, ts[0].tv_sec, ts[0].tv_nsec, ts[1].tv_sec, ts[1].tv_nsec); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); atsecs = ts[0].tv_sec; atnsecs = ts[0].tv_nsec; mtsecs = ts[1].tv_sec; mtnsecs = ts[1].tv_nsec; #ifdef UTIME_NOW if (atnsecs == UTIME_NOW) atnsecs = -1; #endif #ifdef UTIME_OMIT if (atnsecs == UTIME_OMIT) atnsecs = -2; #endif #ifdef UTIME_NOW if (mtnsecs == UTIME_NOW) mtnsecs = -1; #endif #ifdef UTIME_OMIT if (mtnsecs == UTIME_OMIT) mtnsecs = -2; #endif r = guestfs_utimens (g, path, atsecs, atnsecs, mtsecs, mtnsecs); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_getattr (const char *path, struct stat *statbuf) { DECL_G (); DEBUG_CALL ("%s, %p", path, statbuf); const struct stat *buf; buf = lsc_lookup (g, path); if (buf) { memcpy (statbuf, buf, sizeof *statbuf); return 0; } struct guestfs_stat *r; r = guestfs_lstat (g, path); if (r == NULL) RETURN_ERRNO; statbuf->st_dev = r->dev; statbuf->st_ino = r->ino; statbuf->st_mode = r->mode; statbuf->st_nlink = r->nlink; statbuf->st_uid = r->uid; statbuf->st_gid = r->gid; statbuf->st_rdev = r->rdev; statbuf->st_size = r->size; statbuf->st_blksize = r->blksize; statbuf->st_blocks = r->blocks; statbuf->st_atime = r->atime; statbuf->st_mtime = r->mtime; statbuf->st_ctime = r->ctime; guestfs_free_stat (r); return 0; }
static int mount_local_rename (const char *from, const char *to) { DECL_G (); DEBUG_CALL ("%s, %s", from, to); int r; if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, from); dir_cache_invalidate (g, to); /* XXX It's not clear how close the 'mv' command is to the * rename syscall. We might need to add the rename syscall * to the guestfs(3) API. */ r = guestfs_mv (g, from, to); if (r == -1) RETURN_ERRNO; return 0; }
static int mount_local_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { const size_t limit = 2 * 1024 * 1024; int r; DECL_G (); DEBUG_CALL ("%s, %p, %zu, %ld", path, buf, size, (long) offset); if (g->ml_read_only) return -EROFS; dir_cache_invalidate (g, path); /* See mount_local_read. */ if (size > limit) size = limit; r = guestfs_pwrite (g, path, buf, size, offset); if (r == -1) RETURN_ERRNO; return r; }
/* Nautilus loves to use access(2) to test everything about a file, * such as whether it's executable. Therefore treat this a lot like * mount_local_getattr. */ static int mount_local_access (const char *path, int mask) { DECL_G (); DEBUG_CALL ("%s, %d", path, mask); struct stat statbuf; int r; if (g->ml_read_only && (mask & W_OK)) return -EROFS; r = mount_local_getattr (path, &statbuf); if (r < 0 || mask == F_OK) return r; struct fuse_context *fuse = fuse_get_context (); int ok = 1; if (mask & R_OK) ok = ok && ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IRUSR : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IRGRP : statbuf.st_mode & S_IROTH); if (mask & W_OK) ok = ok && ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IWUSR : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IWGRP : statbuf.st_mode & S_IWOTH); if (mask & X_OK) ok = ok && ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IXUSR : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IXGRP : statbuf.st_mode & S_IXOTH); return ok ? 0 : -EACCES; }
static int mount_local_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { time_t now; size_t i; char **names; CLEANUP_FREE_DIRENT_LIST struct guestfs_dirent_list *ents = NULL; DECL_G (); DEBUG_CALL ("%s, %p, %ld", path, buf, (long) offset); time (&now); dir_cache_remove_all_expired (g, now); ents = guestfs_readdir (g, path); if (ents == NULL) RETURN_ERRNO; for (i = 0; i < ents->len; ++i) { struct stat stat; memset (&stat, 0, sizeof stat); stat.st_ino = ents->val[i].ino; switch (ents->val[i].ftyp) { case 'b': stat.st_mode = S_IFBLK; break; case 'c': stat.st_mode = S_IFCHR; break; case 'd': stat.st_mode = S_IFDIR; break; case 'f': stat.st_mode = S_IFIFO; break; case 'l': stat.st_mode = S_IFLNK; break; case 'r': stat.st_mode = S_IFREG; break; case 's': stat.st_mode = S_IFSOCK; break; case 'u': case '?': default: stat.st_mode = 0; } /* Copied from the example, which also ignores 'offset'. I'm * not quite sure how this is ever supposed to work on large * directories. XXX */ if (filler (buf, ents->val[i].name, &stat, 0)) break; } /* Now prepopulate the directory caches. This step is just an * optimization, don't worry if it fails. */ names = malloc ((ents->len + 1) * sizeof (char *)); if (names) { CLEANUP_FREE_STATNS_LIST struct guestfs_statns_list *ss = NULL; CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL; char **links; for (i = 0; i < ents->len; ++i) names[i] = ents->val[i].name; names[i] = NULL; ss = guestfs_lstatnslist (g, path, names); if (ss) { for (i = 0; i < ss->len; ++i) { if (ss->val[i].st_ino >= 0) { struct stat statbuf; memset (&statbuf, 0, sizeof statbuf); statbuf.st_dev = ss->val[i].st_dev; statbuf.st_ino = ss->val[i].st_ino; statbuf.st_mode = ss->val[i].st_mode; statbuf.st_nlink = ss->val[i].st_nlink; statbuf.st_uid = ss->val[i].st_uid; statbuf.st_gid = ss->val[i].st_gid; statbuf.st_rdev = ss->val[i].st_rdev; statbuf.st_size = ss->val[i].st_size; statbuf.st_blksize = ss->val[i].st_blksize; statbuf.st_blocks = ss->val[i].st_blocks; statbuf.st_atime = ss->val[i].st_atime_sec; #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC statbuf.st_atim.tv_nsec = ss->val[i].st_atime_nsec; #endif statbuf.st_mtime = ss->val[i].st_mtime_sec; #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC statbuf.st_mtim.tv_nsec = ss->val[i].st_mtime_nsec; #endif statbuf.st_ctime = ss->val[i].st_ctime_sec; #ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC statbuf.st_ctim.tv_nsec = ss->val[i].st_ctime_nsec; #endif lsc_insert (g, path, names[i], now, &statbuf); } } } xattrs = guestfs_lxattrlist (g, path, names); if (xattrs) { size_t ni, num; struct guestfs_xattr *first; struct guestfs_xattr_list *copy; for (i = 0, ni = 0; i < xattrs->len; ++i, ++ni) { /* assert (strlen (xattrs->val[i].attrname) == 0); */ if (xattrs->val[i].attrval_len > 0) { ++i; first = &xattrs->val[i]; num = 0; for (; i < xattrs->len && strlen (xattrs->val[i].attrname) > 0; ++i) num++; copy = copy_xattr_list (g, first, num); if (copy) xac_insert (g, path, names[ni], now, copy); i--; } } } links = guestfs_readlinklist (g, path, names); if (links) { for (i = 0; names[i] != NULL; ++i) { if (links[i][0]) /* Note that rlc_insert owns the string links[i] after this, */ rlc_insert (g, path, names[i], now, links[i]); else /* which is why we have to free links[i] here. */ free (links[i]); } free (links); /* free the array, not the strings */ } free (names); } return 0; }