Add support for extended access() in Loopback-C

This commit is contained in:
Benjamin Fleischer 2023-10-26 00:03:07 +02:00
parent 8ebe884c72
commit ca5d4b94d9
1 changed files with 193 additions and 149 deletions

View File

@ -1,10 +1,10 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file LICENSE.txt.
*/
/*
@ -17,6 +17,7 @@
#define HAVE_EXCHANGE 0
#define HAVE_RENAMEX 1
#define HAVE_ACCESS 0
#define FUSE_USE_VERSION 26
@ -63,20 +64,20 @@ static int
loopback_getattr(const char *path, struct stat *stbuf)
{
int res;
res = lstat(path, stbuf);
/*
* The optimal I/O size can be set on a per-file basis. Setting st_blksize
* to zero will cause the kernel extension to fall back on the global I/O
* size which can be specified at mount-time (option iosize).
*/
stbuf->st_blksize = 0;
if (res == -1) {
return -errno;
}
return 0;
}
@ -85,33 +86,73 @@ loopback_fgetattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
int res;
(void)path;
res = fstat(fi->fh, stbuf);
// Fall back to global I/O size. See loopback_getattr().
stbuf->st_blksize = 0;
if (res == -1) {
return -errno;
}
return 0;
}
#if HAVE_ACCESS
static int
loopback_access(const char *path, int mask)
{
int res;
/*
* Standard access permission flags:
* F_OK test for existence of file
* X_OK test for execute or search permission
* W_OK test for write permission
* R_OK test for read permission
*
* Extended access permission flags that can be enabled by setting
* FUSE_CAP_ACCESS_EXTENDED (See loopback_init()):
* _READ_OK read file data / read directory
* _WRITE_OK write file data / add file to directory
* _EXECUTE_OK execute file / search in directory
* _DELETE_OK delete file / delete directory
* _APPEND_OK append to file / add subdirectory to directory
* _RMFILE_OK remove file from directory
* _RATTR_OK read basic attributes
* _WATTR_OK write basic attributes
* _REXT_OK read extended attributes
* _WEXT_OK write extended attributes
* _RPERM_OK read permissions
* _WPERM_OK write permissions
* _CHOWN_OK change ownership
*/
res = access(path, mask & (F_OK | X_OK | W_OK | R_OK));
if (res == -1)
return -errno;
return 0;
}
#endif /* HAVE_ACCESS */
static int
loopback_readlink(const char *path, char *buf, size_t size)
{
int res;
res = readlink(path, buf, size - 1);
if (res == -1) {
return -errno;
}
buf[res] = '\0';
return 0;
}
@ -125,24 +166,24 @@ static int
loopback_opendir(const char *path, struct fuse_file_info *fi)
{
int res;
struct loopback_dirp *d = malloc(sizeof(struct loopback_dirp));
if (d == NULL) {
return -ENOMEM;
}
d->dp = opendir(path);
if (d->dp == NULL) {
res = -errno;
free(d);
return res;
}
d->offset = 0;
d->entry = NULL;
fi->fh = (unsigned long)d;
return 0;
}
@ -157,9 +198,9 @@ loopback_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi)
{
struct loopback_dirp *d = get_dirp(fi);
(void)path;
if (offset == 0) {
rewinddir(d->dp);
d->entry = NULL;
@ -170,37 +211,37 @@ loopback_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
d->entry = NULL;
d->offset = offset;
}
while (1) {
struct stat st;
off_t nextoff;
if (!d->entry) {
d->entry = readdir(d->dp);
if (!d->entry) {
break;
}
}
memset(&st, 0, sizeof(st));
st.st_ino = d->entry->d_ino;
st.st_mode = d->entry->d_type << 12;
/*
* Under macOS, telldir() may return 0 the first time it is called.
* But for libfuse, an offset of zero means that offsets are not
* supported, so we shift everything by one.
*/
nextoff = telldir(d->dp) + 1;
if (filler(buf, d->entry->d_name, &st, nextoff)) {
break;
}
d->entry = NULL;
d->offset = nextoff;
}
return 0;
}
@ -208,12 +249,12 @@ static int
loopback_releasedir(const char *path, struct fuse_file_info *fi)
{
struct loopback_dirp *d = get_dirp(fi);
(void)path;
closedir(d->dp);
free(d);
return 0;
}
@ -221,17 +262,17 @@ static int
loopback_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
if (S_ISFIFO(mode)) {
res = mkfifo(path, mode);
} else {
res = mknod(path, mode, rdev);
}
if (res == -1) {
return -errno;
}
return 0;
}
@ -239,12 +280,12 @@ static int
loopback_mkdir(const char *path, mode_t mode)
{
int res;
res = mkdir(path, mode);
if (res == -1) {
return -errno;
}
return 0;
}
@ -252,12 +293,12 @@ static int
loopback_unlink(const char *path)
{
int res;
res = unlink(path);
if (res == -1) {
return -errno;
}
return 0;
}
@ -265,12 +306,12 @@ static int
loopback_rmdir(const char *path)
{
int res;
res = rmdir(path);
if (res == -1) {
return -errno;
}
return 0;
}
@ -278,12 +319,12 @@ static int
loopback_symlink(const char *from, const char *to)
{
int res;
res = symlink(from, to);
if (res == -1) {
return -errno;
}
return 0;
}
@ -291,12 +332,12 @@ static int
loopback_rename(const char *from, const char *to)
{
int res;
res = rename(from, to);
if (res == -1) {
return -errno;
}
return 0;
}
@ -321,12 +362,12 @@ static int
loopback_link(const char *from, const char *to)
{
int res;
res = link(from, to);
if (res == -1) {
return -errno;
}
return 0;
}
@ -337,36 +378,36 @@ loopback_fsetattr_x(const char *path, struct setattr_x *attr,
int res;
uid_t uid = -1;
gid_t gid = -1;
if (SETATTR_WANTS_MODE(attr)) {
res = fchmod(fi->fh, attr->mode);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_UID(attr)) {
uid = attr->uid;
}
if (SETATTR_WANTS_GID(attr)) {
gid = attr->gid;
}
if ((uid != -1) || (gid != -1)) {
res = fchown(fi->fh, uid, gid);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_SIZE(attr)) {
res = ftruncate(fi->fh, attr->size);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_MODTIME(attr)) {
struct timeval tv[2];
if (!SETATTR_WANTS_ACCTIME(attr)) {
@ -382,10 +423,10 @@ loopback_fsetattr_x(const char *path, struct setattr_x *attr,
return -errno;
}
}
if (SETATTR_WANTS_CRTIME(attr)) {
struct attrlist attributes;
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = ATTR_CMN_CRTIME;
@ -393,18 +434,18 @@ loopback_fsetattr_x(const char *path, struct setattr_x *attr,
attributes.fileattr = 0;
attributes.forkattr = 0;
attributes.volattr = 0;
res = fsetattrlist(fi->fh, &attributes, &attr->crtime,
sizeof(struct timespec), FSOPT_NOFOLLOW);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_CHGTIME(attr)) {
struct attrlist attributes;
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = ATTR_CMN_CHGTIME;
@ -412,18 +453,18 @@ loopback_fsetattr_x(const char *path, struct setattr_x *attr,
attributes.fileattr = 0;
attributes.forkattr = 0;
attributes.volattr = 0;
res = fsetattrlist(fi->fh, &attributes, &attr->chgtime,
sizeof(struct timespec), FSOPT_NOFOLLOW);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_BKUPTIME(attr)) {
struct attrlist attributes;
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = ATTR_CMN_BKUPTIME;
@ -431,22 +472,22 @@ loopback_fsetattr_x(const char *path, struct setattr_x *attr,
attributes.fileattr = 0;
attributes.forkattr = 0;
attributes.volattr = 0;
res = fsetattrlist(fi->fh, &attributes, &attr->bkuptime,
sizeof(struct timespec), FSOPT_NOFOLLOW);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_FLAGS(attr)) {
res = fchflags(fi->fh, attr->flags);
if (res == -1) {
return -errno;
}
}
return 0;
}
@ -456,36 +497,36 @@ loopback_setattr_x(const char *path, struct setattr_x *attr)
int res;
uid_t uid = -1;
gid_t gid = -1;
if (SETATTR_WANTS_MODE(attr)) {
res = lchmod(path, attr->mode);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_UID(attr)) {
uid = attr->uid;
}
if (SETATTR_WANTS_GID(attr)) {
gid = attr->gid;
}
if ((uid != -1) || (gid != -1)) {
res = lchown(path, uid, gid);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_SIZE(attr)) {
res = truncate(path, attr->size);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_MODTIME(attr)) {
struct timeval tv[2];
if (!SETATTR_WANTS_ACCTIME(attr)) {
@ -501,10 +542,10 @@ loopback_setattr_x(const char *path, struct setattr_x *attr)
return -errno;
}
}
if (SETATTR_WANTS_CRTIME(attr)) {
struct attrlist attributes;
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = ATTR_CMN_CRTIME;
@ -512,18 +553,18 @@ loopback_setattr_x(const char *path, struct setattr_x *attr)
attributes.fileattr = 0;
attributes.forkattr = 0;
attributes.volattr = 0;
res = setattrlist(path, &attributes, &attr->crtime,
sizeof(struct timespec), FSOPT_NOFOLLOW);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_CHGTIME(attr)) {
struct attrlist attributes;
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = ATTR_CMN_CHGTIME;
@ -531,18 +572,18 @@ loopback_setattr_x(const char *path, struct setattr_x *attr)
attributes.fileattr = 0;
attributes.forkattr = 0;
attributes.volattr = 0;
res = setattrlist(path, &attributes, &attr->chgtime,
sizeof(struct timespec), FSOPT_NOFOLLOW);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_BKUPTIME(attr)) {
struct attrlist attributes;
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = ATTR_CMN_BKUPTIME;
@ -550,22 +591,22 @@ loopback_setattr_x(const char *path, struct setattr_x *attr)
attributes.fileattr = 0;
attributes.forkattr = 0;
attributes.volattr = 0;
res = setattrlist(path, &attributes, &attr->bkuptime,
sizeof(struct timespec), FSOPT_NOFOLLOW);
if (res == -1) {
return -errno;
}
}
if (SETATTR_WANTS_FLAGS(attr)) {
res = lchflags(path, attr->flags);
if (res == -1) {
return -errno;
}
}
return 0;
}
@ -575,7 +616,7 @@ loopback_getxtimes(const char *path, struct timespec *bkuptime,
{
int res = 0;
struct attrlist attributes;
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
attributes.reserved = 0;
attributes.commonattr = 0;
@ -583,17 +624,14 @@ loopback_getxtimes(const char *path, struct timespec *bkuptime,
attributes.fileattr = 0;
attributes.forkattr = 0;
attributes.volattr = 0;
struct xtimeattrbuf {
uint32_t size;
struct timespec xtime;
} __attribute__ ((packed));
struct xtimeattrbuf buf;
attributes.commonattr = ATTR_CMN_BKUPTIME;
res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW);
if (res == 0) {
@ -601,7 +639,7 @@ loopback_getxtimes(const char *path, struct timespec *bkuptime,
} else {
(void)memset(bkuptime, 0, sizeof(struct timespec));
}
attributes.commonattr = ATTR_CMN_CRTIME;
res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW);
if (res == 0) {
@ -609,7 +647,7 @@ loopback_getxtimes(const char *path, struct timespec *bkuptime,
} else {
(void)memset(crtime, 0, sizeof(struct timespec));
}
return 0;
}
@ -617,12 +655,12 @@ static int
loopback_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags, mode);
if (fd == -1) {
return -errno;
}
fi->fh = fd;
return 0;
}
@ -631,12 +669,12 @@ static int
loopback_open(const char *path, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags);
if (fd == -1) {
return -errno;
}
fi->fh = fd;
return 0;
}
@ -646,13 +684,13 @@ loopback_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int res;
(void)path;
res = pread(fi->fh, buf, size, offset);
if (res == -1) {
res = -errno;
}
return res;
}
@ -661,14 +699,14 @@ loopback_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res;
(void)path;
res = pwrite(fi->fh, buf, size, offset);
if (res == -1) {
res = -errno;
}
return res;
}
@ -676,14 +714,14 @@ static int
loopback_flush(const char *path, struct fuse_file_info *fi)
{
int res;
(void)path;
res = close(dup(fi->fh));
if (res == -1) {
return -errno;
}
return 0;
}
@ -691,9 +729,9 @@ static int
loopback_release(const char *path, struct fuse_file_info *fi)
{
(void)path;
close(fi->fh);
return 0;
}
@ -701,16 +739,16 @@ static int
loopback_fsync(const char *path, int isdatasync, struct fuse_file_info *fi)
{
int res;
(void)path;
(void)isdatasync;
res = fsync(fi->fh);
if (res == -1) {
return -errno;
}
return 0;
}
@ -719,28 +757,28 @@ loopback_setxattr(const char *path, const char *name, const char *value,
size_t size, int flags, uint32_t position)
{
int res;
if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) {
flags &= ~(XATTR_NOSECURITY);
}
if (!strcmp(name, A_KAUTH_FILESEC_XATTR)) {
char new_name[MAXPATHLEN];
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
res = setxattr(path, new_name, value, size, position, XATTR_NOFOLLOW);
} else {
res = setxattr(path, name, value, size, position, XATTR_NOFOLLOW);
}
if (res == -1) {
return -errno;
}
return 0;
}
@ -749,24 +787,24 @@ loopback_getxattr(const char *path, const char *name, char *value, size_t size,
uint32_t position)
{
int res;
if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) {
char new_name[MAXPATHLEN];
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
res = getxattr(path, new_name, value, size, position, XATTR_NOFOLLOW);
} else {
res = getxattr(path, name, value, size, position, XATTR_NOFOLLOW);
}
if (res == -1) {
return -errno;
}
return res;
}
@ -798,11 +836,11 @@ loopback_listxattr(const char *path, char *list, size_t size)
*/
}
}
if (res == -1) {
return -errno;
}
return res;
}
@ -810,24 +848,24 @@ static int
loopback_removexattr(const char *path, const char *name)
{
int res;
if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) {
char new_name[MAXPATHLEN];
memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
res = removexattr(path, new_name, XATTR_NOFOLLOW);
} else {
res = removexattr(path, name, XATTR_NOFOLLOW);
}
if (res == -1) {
return -errno;
}
return 0;
}
@ -836,11 +874,11 @@ loopback_fallocate(const char *path, int mode, off_t offset, off_t length,
struct fuse_file_info *fi)
{
fstore_t fstore;
if (!(mode & PREALLOCATE)) {
return -ENOTSUP;
}
fstore.fst_flags = 0;
if (mode & ALLOCATECONTIG) {
fstore.fst_flags |= F_ALLOCATECONTIG;
@ -848,16 +886,16 @@ loopback_fallocate(const char *path, int mode, off_t offset, off_t length,
if (mode & ALLOCATEALL) {
fstore.fst_flags |= F_ALLOCATEALL;
}
if (mode & ALLOCATEFROMPEOF) {
fstore.fst_posmode = F_PEOFPOSMODE;
} else if (mode & ALLOCATEFROMVOL) {
fstore.fst_posmode = F_VOLPOSMODE;
}
fstore.fst_offset = offset;
fstore.fst_length = length;
if (fcntl(fi->fh, F_PREALLOCATE, &fstore) == -1) {
return -errno;
} else {
@ -875,12 +913,12 @@ static int
loopback_statfs_x(const char *path, struct statfs *stbuf)
{
int res;
res = statfs(path, stbuf);
if (res == -1) {
return -errno;
}
stbuf->f_blocks = stbuf->f_blocks * stbuf->f_bsize / loopback.blocksize;
stbuf->f_bavail = stbuf->f_bavail * stbuf->f_bsize / loopback.blocksize;
stbuf->f_bfree = stbuf->f_bfree * stbuf->f_bsize / loopback.blocksize;
@ -911,6 +949,10 @@ loopback_init(struct fuse_conn_info *conn)
{
conn->want |= FUSE_CAP_VOL_RENAME | FUSE_CAP_XTIMES | FUSE_CAP_NODE_RWLOCK;
#if HAVE_ACCESS
conn->want |= FUSE_CAP_ACCESS_EXTENDED;
#endif
#ifdef FUSE_ENABLE_CASE_INSENSITIVE
if (loopback.case_insensitive) {
conn->want |= FUSE_CAP_CASE_INSENSITIVE;
@ -931,7 +973,9 @@ static struct fuse_operations loopback_oper = {
.destroy = loopback_destroy,
.getattr = loopback_getattr,
.fgetattr = loopback_fgetattr,
/* .access = loopback_access, */
#if HAVE_ACCESS
.access = loopback_access,
#endif
.readlink = loopback_readlink,
.opendir = loopback_opendir,
.readdir = loopback_readdir,
@ -966,7 +1010,7 @@ static struct fuse_operations loopback_oper = {
#if HAVE_RENAMEX
.renamex = loopback_renamex,
#endif
.flag_nullpath_ok = 1,
.flag_nopath = 1,
};
@ -982,16 +1026,16 @@ main(int argc, char *argv[])
{
int res = 0;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
loopback.blocksize = 4096;
loopback.case_insensitive = 0;
if (fuse_opt_parse(&args, &loopback, loopback_opts, NULL) == -1) {
exit(1);
}
umask(0);
res = fuse_main(args.argc, args.argv, &loopback_oper, NULL);
fuse_opt_free_args(&args);
return res;
}