990 lines
21 KiB
C
990 lines
21 KiB
C
/*
|
|
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.
|
|
|
|
*/
|
|
|
|
/*
|
|
* Loopback macFUSE file system in C. Uses the high-level FUSE API.
|
|
* Based on the fusexmp_fh.c example from the Linux FUSE distribution.
|
|
* Amit Singh <http://osxbook.com>
|
|
*/
|
|
|
|
#include <AvailabilityMacros.h>
|
|
|
|
#define HAVE_EXCHANGE 0
|
|
#define HAVE_RENAMEX 1
|
|
|
|
#define FUSE_USE_VERSION 26
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <fuse.h>
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/attr.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/xattr.h>
|
|
#include <unistd.h>
|
|
|
|
#if defined(_POSIX_C_SOURCE)
|
|
typedef unsigned char u_char;
|
|
typedef unsigned short u_short;
|
|
typedef unsigned int u_int;
|
|
typedef unsigned long u_long;
|
|
#endif
|
|
|
|
#define G_PREFIX "org"
|
|
#define G_KAUTH_FILESEC_XATTR G_PREFIX ".apple.system.Security"
|
|
#define A_PREFIX "com"
|
|
#define A_KAUTH_FILESEC_XATTR A_PREFIX ".apple.system.Security"
|
|
#define XATTR_APPLE_PREFIX "com.apple."
|
|
|
|
struct loopback {
|
|
int case_insensitive;
|
|
};
|
|
|
|
static struct loopback loopback;
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
struct loopback_dirp {
|
|
DIR *dp;
|
|
struct dirent *entry;
|
|
off_t offset;
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
static inline struct loopback_dirp *
|
|
get_dirp(struct fuse_file_info *fi)
|
|
{
|
|
return (struct loopback_dirp *)(uintptr_t)fi->fh;
|
|
}
|
|
|
|
static int
|
|
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;
|
|
d->offset = 0;
|
|
} else if (offset != d->offset) {
|
|
// Subtract the one that we add when calling telldir() below
|
|
seekdir(d->dp, offset - 1);
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
loopback_mkdir(const char *path, mode_t mode)
|
|
{
|
|
int res;
|
|
|
|
res = mkdir(path, mode);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loopback_unlink(const char *path)
|
|
{
|
|
int res;
|
|
|
|
res = unlink(path);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loopback_rmdir(const char *path)
|
|
{
|
|
int res;
|
|
|
|
res = rmdir(path);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loopback_symlink(const char *from, const char *to)
|
|
{
|
|
int res;
|
|
|
|
res = symlink(from, to);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loopback_rename(const char *from, const char *to)
|
|
{
|
|
int res;
|
|
|
|
res = rename(from, to);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if HAVE_EXCHANGE
|
|
|
|
static int
|
|
loopback_exchange(const char *path1, const char *path2, unsigned long options)
|
|
{
|
|
int res;
|
|
|
|
res = exchangedata(path1, path2, options);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_EXCHANGE */
|
|
|
|
static int
|
|
loopback_link(const char *from, const char *to)
|
|
{
|
|
int res;
|
|
|
|
res = link(from, to);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loopback_fsetattr_x(const char *path, struct setattr_x *attr,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
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)) {
|
|
gettimeofday(&tv[0], NULL);
|
|
} else {
|
|
tv[0].tv_sec = attr->acctime.tv_sec;
|
|
tv[0].tv_usec = attr->acctime.tv_nsec / 1000;
|
|
}
|
|
tv[1].tv_sec = attr->modtime.tv_sec;
|
|
tv[1].tv_usec = attr->modtime.tv_nsec / 1000;
|
|
res = futimes(fi->fh, tv);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
}
|
|
|
|
if (SETATTR_WANTS_CRTIME(attr)) {
|
|
struct attrlist attributes;
|
|
|
|
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
attributes.reserved = 0;
|
|
attributes.commonattr = ATTR_CMN_CRTIME;
|
|
attributes.dirattr = 0;
|
|
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;
|
|
attributes.dirattr = 0;
|
|
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;
|
|
attributes.dirattr = 0;
|
|
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;
|
|
}
|
|
|
|
static int
|
|
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)) {
|
|
gettimeofday(&tv[0], NULL);
|
|
} else {
|
|
tv[0].tv_sec = attr->acctime.tv_sec;
|
|
tv[0].tv_usec = attr->acctime.tv_nsec / 1000;
|
|
}
|
|
tv[1].tv_sec = attr->modtime.tv_sec;
|
|
tv[1].tv_usec = attr->modtime.tv_nsec / 1000;
|
|
res = lutimes(path, tv);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
}
|
|
|
|
if (SETATTR_WANTS_CRTIME(attr)) {
|
|
struct attrlist attributes;
|
|
|
|
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
attributes.reserved = 0;
|
|
attributes.commonattr = ATTR_CMN_CRTIME;
|
|
attributes.dirattr = 0;
|
|
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;
|
|
attributes.dirattr = 0;
|
|
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;
|
|
attributes.dirattr = 0;
|
|
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;
|
|
}
|
|
|
|
static int
|
|
loopback_getxtimes(const char *path, struct timespec *bkuptime,
|
|
struct timespec *crtime)
|
|
{
|
|
int res = 0;
|
|
struct attrlist attributes;
|
|
|
|
attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
attributes.reserved = 0;
|
|
attributes.commonattr = 0;
|
|
attributes.dirattr = 0;
|
|
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) {
|
|
(void)memcpy(bkuptime, &(buf.xtime), sizeof(struct timespec));
|
|
} 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) {
|
|
(void)memcpy(crtime, &(buf.xtime), sizeof(struct timespec));
|
|
} else {
|
|
(void)memset(crtime, 0, sizeof(struct timespec));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
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;
|
|
}
|
|
|
|
static int
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
loopback_release(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
(void)path;
|
|
|
|
close(fi->fh);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
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;
|
|
}
|
|
|
|
static int
|
|
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;
|
|
}
|
|
|
|
static int
|
|
loopback_listxattr(const char *path, char *list, size_t size)
|
|
{
|
|
ssize_t res = listxattr(path, list, size, XATTR_NOFOLLOW);
|
|
if (res > 0) {
|
|
if (list) {
|
|
size_t len = 0;
|
|
char *curr = list;
|
|
do {
|
|
size_t thislen = strlen(curr) + 1;
|
|
if (strcmp(curr, G_KAUTH_FILESEC_XATTR) == 0) {
|
|
memmove(curr, curr + thislen, res - len - thislen);
|
|
res -= thislen;
|
|
break;
|
|
}
|
|
curr += thislen;
|
|
len += thislen;
|
|
} while (len < res);
|
|
} else {
|
|
/*
|
|
ssize_t res2 = getxattr(path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0,
|
|
XATTR_NOFOLLOW);
|
|
if (res2 >= 0) {
|
|
res -= sizeof(G_KAUTH_FILESEC_XATTR);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
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;
|
|
}
|
|
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 {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
loopback_setvolname(const char *name)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loopback_statfs_x(const char *path, struct statfs *stbuf)
|
|
{
|
|
int res;
|
|
|
|
res = statfs(path, stbuf);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if HAVE_RENAMEX
|
|
|
|
static int
|
|
loopback_renamex(const char *path1, const char *path2, unsigned int flags)
|
|
{
|
|
int res;
|
|
|
|
res = renamex_np(path1, path2, flags);
|
|
if (res == -1) {
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_RENAMEX */
|
|
|
|
void *
|
|
loopback_init(struct fuse_conn_info *conn)
|
|
{
|
|
FUSE_ENABLE_SETVOLNAME(conn);
|
|
FUSE_ENABLE_XTIMES(conn);
|
|
|
|
#ifdef FUSE_ENABLE_CASE_INSENSITIVE
|
|
if (loopback.case_insensitive) {
|
|
FUSE_ENABLE_CASE_INSENSITIVE(conn);
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
loopback_destroy(void *userdata)
|
|
{
|
|
/* nothing */
|
|
}
|
|
|
|
static struct fuse_operations loopback_oper = {
|
|
.init = loopback_init,
|
|
.destroy = loopback_destroy,
|
|
.getattr = loopback_getattr,
|
|
.fgetattr = loopback_fgetattr,
|
|
/* .access = loopback_access, */
|
|
.readlink = loopback_readlink,
|
|
.opendir = loopback_opendir,
|
|
.readdir = loopback_readdir,
|
|
.releasedir = loopback_releasedir,
|
|
.mknod = loopback_mknod,
|
|
.mkdir = loopback_mkdir,
|
|
.symlink = loopback_symlink,
|
|
.unlink = loopback_unlink,
|
|
.rmdir = loopback_rmdir,
|
|
.rename = loopback_rename,
|
|
.link = loopback_link,
|
|
.create = loopback_create,
|
|
.open = loopback_open,
|
|
.read = loopback_read,
|
|
.write = loopback_write,
|
|
.flush = loopback_flush,
|
|
.release = loopback_release,
|
|
.fsync = loopback_fsync,
|
|
.setxattr = loopback_setxattr,
|
|
.getxattr = loopback_getxattr,
|
|
.listxattr = loopback_listxattr,
|
|
.removexattr = loopback_removexattr,
|
|
#if HAVE_EXCHANGE
|
|
.exchange = loopback_exchange,
|
|
#endif
|
|
.getxtimes = loopback_getxtimes,
|
|
.setattr_x = loopback_setattr_x,
|
|
.fsetattr_x = loopback_fsetattr_x,
|
|
.fallocate = loopback_fallocate,
|
|
.setvolname = loopback_setvolname,
|
|
.statfs_x = loopback_statfs_x,
|
|
#if HAVE_RENAMEX
|
|
.renamex = loopback_renamex,
|
|
#endif
|
|
|
|
.flag_nullpath_ok = 1,
|
|
.flag_nopath = 1,
|
|
};
|
|
|
|
static const struct fuse_opt loopback_opts[] = {
|
|
{ "case_insensitive", offsetof(struct loopback, case_insensitive), 1 },
|
|
FUSE_OPT_END
|
|
};
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int res = 0;
|
|
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
|
|
|
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;
|
|
}
|