Co-authored-by: Chris Darroch <chrisd8088@github.com>
This commit is contained in:
Ashe Connor 2019-01-30 11:46:40 +11:00
Родитель 6b08d4f237
Коммит 9b3b975fc6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 997E657B1C52B6C5
5 изменённых файлов: 348 добавлений и 23 удалений

Просмотреть файл

@ -111,6 +111,28 @@ int projfs_start(struct projfs *fs);
*/
void *projfs_stop(struct projfs *fs);
/**
* Create a directory whose contents will be projected until written.
*
* @param[in] fs Projected filesystem handle.
* @param[in] path Relative path of new directory under projfs mount point.
* @return Zero on success or an \p errno(3) code on failure.
*/
int projfs_create_proj_dir(struct projfs *fs, const char *path);
/**
* Create a file whose contents will be projected until written.
*
* @param[in] fs Projected filesystem handle.
* @param[in] path Relative path of new file under projfs mount point.
* @param[in] size File size to be projected until file is written.
* @param[in] mode File mode with which to create the new projected file.
* TODO: add extra arg for private user data
* @return Zero on success or an \p errno(3) code on failure.
*/
int projfs_create_proj_file(struct projfs *fs, const char *path, off_t size,
mode_t mode);
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -156,16 +156,13 @@ PrjFS_Result PrjFS_ConvertDirectoryToVirtualizationRoot(
_In_ const char* virtualizationRootFullPath
);
#if 0
PrjFS_Result PrjFS_ConvertDirectoryToPlaceholder(
_In_ const char* relativePath
);
PrjFS_Result PrjFS_WritePlaceholderDirectory(
_In_ const PrjFS_MountHandle* mountHandle,
_In_ const char* relativePath
);
PrjFS_Result PrjFS_WritePlaceholderFile(
_In_ const PrjFS_MountHandle* mountHandle,
_In_ const char* relativePath,
_In_ unsigned char providerId[PrjFS_PlaceholderIdLength],
_In_ unsigned char contentId[PrjFS_PlaceholderIdLength],
@ -173,6 +170,12 @@ PrjFS_Result PrjFS_WritePlaceholderFile(
_In_ uint16_t fileMode
);
#if 0
PrjFS_Result PrjFS_ConvertDirectoryToPlaceholder(
_In_ const char* relativePath
);
PrjFS_Result PrjFS_WriteSymLink(
_In_ const char* relativePath,
_In_ const char* symLinkTarget

Просмотреть файл

@ -46,6 +46,7 @@
#endif
#define lowerdir_fd() (projfs_context_fs()->lowerdir_fd)
#define PROJ_DIR_MODE 0777
struct projfs_dir {
DIR *dir;
@ -91,15 +92,14 @@ static int projfs_fuse_send_event(projfs_handler_t handler,
}
static int projfs_fuse_proj_event(uint64_t mask,
const char *base_path,
const char *name,
const char *path,
int fd)
{
projfs_handler_t handler =
projfs_context_fs()->handlers.handle_proj_event;
return projfs_fuse_send_event(
handler, mask, base_path, name, fd, 0);
handler, mask, path, NULL, fd, 0);
}
static int projfs_fuse_notify_event(uint64_t mask,
@ -124,7 +124,114 @@ static int projfs_fuse_perm_event(uint64_t mask,
handler, mask, path, target_path, 0, 1);
}
// filesystem ops
struct node_userdata
{
uint8_t proj_flag; // USER_PROJECTION_EMPTY
};
#define USER_PROJECTION_EMPTY "user.projection.empty"
static __thread char *mapped_path;
static void set_mapped_path(char const *path, int parent)
{
free(mapped_path);
if (!parent)
mapped_path = strdup(path);
const char *last = strrchr(path, '/');
if (!last)
mapped_path = strdup(".");
else
mapped_path = strndup(path, last - path);
}
static struct node_userdata *get_path_userdata_locked(int parent)
{
struct node_userdata *user =
(struct node_userdata *)fuse_get_context_node_userdata(parent);
if (user)
return user;
user = calloc(1, sizeof(*user));
if (!user)
abort();
// fill cache from xattrs
ssize_t sz = lgetxattr(mapped_path, USER_PROJECTION_EMPTY, NULL, 0);
user->proj_flag = sz > 0;
fuse_set_context_node_userdata(parent, user, free);
return user;
}
static struct node_userdata *get_path_userdata(int parent)
{
struct node_userdata *user =
(struct node_userdata *)fuse_get_context_node_userdata(parent);
if (user)
return user;
pthread_mutex_t *user_lock =
fuse_get_context_node_userdata_lock(parent);
if (pthread_mutex_lock(user_lock) != 0)
abort();
user = get_path_userdata_locked(parent);
pthread_mutex_unlock(user_lock);
return user;
}
static int projfs_fuse_proj_dir_locked(struct node_userdata *user,
const char *path)
{
if (!user->proj_flag)
return 0;
int err = projfs_fuse_proj_event(
PROJFS_CREATE_SELF | PROJFS_ONDIR,
path,
0);
if (err < 0)
return -err;
user->proj_flag = 0;
lremovexattr(mapped_path, USER_PROJECTION_EMPTY);
return 0;
}
/**
* Project a directory. Takes the lower path, and a flag indicating whether the
* directory is the parent of the path, or the path itself.
*
* @param path the lower path (from lower_path)
* @param parent 1 if we should look at the parent directory containing path, 0
* if we look at path itself
*/
static int projfs_fuse_proj_dir(const char *path, int parent)
{
set_mapped_path(path, parent);
struct node_userdata *user = get_path_userdata(parent);
if (!user->proj_flag)
return 0;
pthread_mutex_t *user_lock =
fuse_get_context_node_userdata_lock(parent);
if (pthread_mutex_lock(user_lock) != 0)
abort();
int res = projfs_fuse_proj_dir_locked(user, path);
pthread_mutex_unlock(user_lock);
return res;
}
static const char *dotpath = ".";
@ -137,6 +244,8 @@ static inline const char *lowerpath(const char *path)
return path;
}
// filesystem ops
static int projfs_op_getattr(char const *path, struct stat *attr,
struct fuse_file_info *fi)
{
@ -144,15 +253,22 @@ static int projfs_op_getattr(char const *path, struct stat *attr,
int res;
if (fi)
res = fstat(fi->fh, attr);
else
else {
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = fstatat(lowerdir_fd(), lowerpath(path), attr,
AT_SYMLINK_NOFOLLOW);
}
return res == -1 ? -errno : 0;
}
static int projfs_op_readlink(char const *path, char *buf, size_t size)
{
int res = readlinkat(lowerdir_fd(), lowerpath(path), buf, size - 1);
int res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = readlinkat(lowerdir_fd(), lowerpath(path), buf, size - 1);
if (res == -1)
return -errno;
buf[res] = 0;
@ -166,8 +282,14 @@ static int projfs_op_link(char const *src, char const *dst)
/* NOTE: We require lowerdir to be a directory, so this should
* fail when src is an empty path, as we expect.
*/
int res = linkat(lowerdir_fd, lowerpath(src),
lowerdir_fd, lowerpath(dst), 0);
int res = projfs_fuse_proj_dir(lowerpath(src), 1);
if (res)
return -res;
res = projfs_fuse_proj_dir(lowerpath(dst), 1);
if (res)
return -res;
res = linkat(lowerdir_fd, lowerpath(src),
lowerdir_fd, lowerpath(dst), 0);
return res == -1 ? -errno : 0;
}
@ -204,7 +326,9 @@ static int projfs_op_fsync(char const *path, int datasync,
static int projfs_op_mknod(char const *path, mode_t mode, dev_t rdev)
{
int res;
int res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
if (S_ISFIFO(mode))
res = mkfifoat(lowerdir_fd(), lowerpath(path), mode);
else
@ -214,13 +338,19 @@ static int projfs_op_mknod(char const *path, mode_t mode, dev_t rdev)
static int projfs_op_symlink(char const *link, char const *path)
{
int res = symlinkat(link, lowerdir_fd(), lowerpath(path));
int res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = symlinkat(link, lowerdir_fd(), lowerpath(path));
return res == -1 ? -errno : 0;
}
static int projfs_op_create(char const *path, mode_t mode,
struct fuse_file_info *fi)
{
int res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
int flags = fi->flags & ~O_NOFOLLOW;
int fd = openat(lowerdir_fd(), lowerpath(path), flags, mode);
@ -228,7 +358,7 @@ static int projfs_op_create(char const *path, mode_t mode,
return -errno;
fi->fh = fd;
int res = projfs_fuse_notify_event(
res = projfs_fuse_notify_event(
PROJFS_CREATE_SELF,
path,
NULL);
@ -237,6 +367,9 @@ static int projfs_op_create(char const *path, mode_t mode,
static int projfs_op_open(char const *path, struct fuse_file_info *fi)
{
int res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
int flags = fi->flags & ~O_NOFOLLOW;
int fd = openat(lowerdir_fd(), lowerpath(path), flags);
@ -302,6 +435,9 @@ static int projfs_op_unlink(char const *path)
NULL);
if (res < 0)
return res;
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = unlinkat(lowerdir_fd(), lowerpath(path), 0);
return res == -1 ? -errno : 0;
@ -309,7 +445,10 @@ static int projfs_op_unlink(char const *path)
static int projfs_op_mkdir(char const *path, mode_t mode)
{
int res = mkdirat(lowerdir_fd(), lowerpath(path), mode);
int res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = mkdirat(lowerdir_fd(), lowerpath(path), mode);
if (res == -1)
return -errno;
@ -328,6 +467,9 @@ static int projfs_op_rmdir(char const *path)
NULL);
if (res < 0)
return res;
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = unlinkat(lowerdir_fd(), lowerpath(path), AT_REMOVEDIR);
return res == -1 ? -errno : 0;
@ -336,8 +478,14 @@ static int projfs_op_rmdir(char const *path)
static int projfs_op_rename(char const *src, char const *dst,
unsigned int flags)
{
int res = projfs_fuse_proj_dir(lowerpath(src), 1);
if (res)
return -res;
res = projfs_fuse_proj_dir(lowerpath(dst), 1);
if (res)
return -res;
// TODO: may prevent us compiling on BSD; would renameat() suffice?
int res = syscall(
res = syscall(
SYS_renameat2,
lowerdir_fd(),
lowerpath(src),
@ -352,6 +500,13 @@ static int projfs_op_opendir(char const *path, struct fuse_file_info *fi)
int res = 0;
int err = 0;
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = projfs_fuse_proj_dir(lowerpath(path), 0);
if (res)
return -res;
struct projfs_dir *d = calloc(1, sizeof(*d));
if (!d) {
res = -1;
@ -452,8 +607,12 @@ static int projfs_op_chmod(char const *path, mode_t mode,
int res;
if (fi)
res = fchmod(fi->fh, mode);
else
else {
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = fchmodat(lowerdir_fd(), lowerpath(path), mode, 0);
}
return res == -1 ? -errno : 0;
}
@ -463,10 +622,14 @@ static int projfs_op_chown(char const *path, uid_t uid, gid_t gid,
int res;
if (fi)
res = fchown(fi->fh, uid, gid);
else
else {
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
// disallow chown() on lowerdir itself, so no AT_EMPTY_PATH
res = fchownat(lowerdir_fd(), lowerpath(path), uid, gid,
AT_SYMLINK_NOFOLLOW);
}
return res == -1 ? -errno : 0;
}
@ -477,6 +640,10 @@ static int projfs_op_truncate(char const *path, off_t off,
if (fi)
res = ftruncate(fi->fh, off);
else {
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
int fd = openat(lowerdir_fd(), lowerpath(path),
O_WRONLY);
if (fd == -1) {
@ -500,9 +667,13 @@ static int projfs_op_utimens(char const *path, const struct timespec tv[2],
int res;
if (fi)
res = futimens(fi->fh, tv);
else
else {
res = projfs_fuse_proj_dir(lowerpath(path), 1);
if (res)
return -res;
res = utimensat(lowerdir_fd(), lowerpath(path), tv,
AT_SYMLINK_NOFOLLOW);
}
return res == -1 ? -errno : 0;
}
@ -514,6 +685,11 @@ static int projfs_op_setxattr(char const *path, char const *name,
int err = 0;
path = lowerpath(path);
res = projfs_fuse_proj_dir(path, 1);
if (res)
return -res;
int fd = openat(lowerdir_fd(), path, O_WRONLY | O_NONBLOCK);
if (fd == -1)
goto out;
@ -534,6 +710,11 @@ static int projfs_op_getxattr(char const *path, char const *name,
int err = 0;
path = lowerpath(path);
res = projfs_fuse_proj_dir(path, 1);
if (res)
return -res;
int fd = openat(lowerdir_fd(), path, O_RDONLY | O_NONBLOCK);
if (fd == -1)
goto out;
@ -553,6 +734,11 @@ static int projfs_op_listxattr(char const *path, char *list, size_t size)
int err = 0;
path = lowerpath(path);
res = projfs_fuse_proj_dir(path, 1);
if (res)
return -res;
int fd = openat(lowerdir_fd(), path, O_RDONLY | O_NONBLOCK);
if (fd == -1)
goto out;
@ -572,6 +758,11 @@ static int projfs_op_removexattr(char const *path, char const *name)
int err = 0;
path = lowerpath(path);
res = projfs_fuse_proj_dir(path, 1);
if (res)
return -res;
int fd = openat(lowerdir_fd(), path, O_WRONLY | O_NONBLOCK);
if (fd == -1)
goto out;
@ -587,8 +778,11 @@ out:
static int projfs_op_access(char const *path, int mode)
{
int res = faccessat(lowerdir_fd(), lowerpath(path), mode,
AT_SYMLINK_NOFOLLOW);
int res = projfs_fuse_proj_dir(path, 1);
if (res)
return -res;
res = faccessat(lowerdir_fd(), lowerpath(path), mode,
AT_SYMLINK_NOFOLLOW);
return res == -1 ? -errno : 0;
}
@ -877,3 +1071,62 @@ void *projfs_stop(struct projfs *fs)
free(fs);
return user_data;
}
static int check_safe_rel_path(const char *path)
{
const char *s = path;
const char *t;
if (path == NULL || *path == '/')
return 0;
while ((s = strstr(s, "..")) != NULL) {
t = s + 2;
if ((*t == '\0' || *t == '/') &&
(s == path || *(s - 1) == '/'))
return 0;
s += 2;
}
return 1;
}
int projfs_create_proj_dir(struct projfs *fs, const char *path)
{
if (!check_safe_rel_path(path))
return EINVAL;
return _projfs_make_dir(fs, path, PROJ_DIR_MODE, 1);
}
int projfs_create_proj_file(struct projfs *fs, const char *path, off_t size,
mode_t mode)
{
if (!check_safe_rel_path(path))
return EINVAL;
// TODO: return _projfs_create_file(fs, path, size, mode, ...)
// until then, prevent compiler warnings
(void)fs;
(void)size;
(void)mode;
return 0;
}
int _projfs_make_dir(struct projfs *fs, const char *path, mode_t mode,
uint8_t proj_flag)
{
(void)fs;
int res = mkdir(path, mode);
if (res == -1)
return -errno;
if (proj_flag) {
char v = 1;
res = lsetxattr(path, USER_PROJECTION_EMPTY, &v, 1, 0);
if (res == -1)
return -errno;
}
return 0;
}

Просмотреть файл

@ -38,5 +38,18 @@ struct projfs {
/** Private event handler type */
typedef int (*projfs_handler_t)(struct projfs_event *);
/**
* Create a new directory with the given projection flag.
*
* @param[in] fs Projected filesystem handle.
* @param[in] path Relative path of new directory under projfs mount point.
* @param[in] mode File mode with which to create the new directory.
* @param[in] proj_flag True if directory is projected; false otherwise.
* @return Zero on success or an \p errno(3) code on failure.
*/
int _projfs_make_dir(struct projfs *fs, const char *path, mode_t mode,
uint8_t proj_flag);
#endif /* PROJFS_I_H */

Просмотреть файл

@ -378,3 +378,37 @@ PrjFS_Result PrjFS_ConvertDirectoryToVirtualizationRoot(
return PrjFS_Result_Success;
}
PrjFS_Result PrjFS_WritePlaceholderDirectory(
_In_ const PrjFS_MountHandle* mountHandle,
_In_ const char* relativePath
)
{
struct projfs *fs = (struct projfs *) mountHandle;
int ret;
fprintf(stderr, "create proj dir %s\n", relativePath);
ret = projfs_create_proj_dir(fs, relativePath);
return convert_errno_to_result(ret);
}
PrjFS_Result PrjFS_WritePlaceholderFile(
_In_ const PrjFS_MountHandle* mountHandle,
_In_ const char* relativePath,
_In_ unsigned char providerId[PrjFS_PlaceholderIdLength],
_In_ unsigned char contentId[PrjFS_PlaceholderIdLength],
_In_ unsigned long fileSize,
_In_ uint16_t fileMode
)
{
struct projfs *fs = (struct projfs *) mountHandle;
int ret;
// TODO: need to wrap the content+provider IDs into a private struct
// until then, prevent compiler warnings
ret = projfs_create_proj_file(fs, relativePath, fileSize, fileMode);
(void)providerId;
(void)contentId;
return convert_errno_to_result(ret);
}