2019-12-12 15:09:14 +01:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
/*
|
|
|
|
* VirtualBox Guest Shared Folders support: Directory inode and file operations
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006-2018 Oracle Corporation
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/vbox_utils.h>
|
|
|
|
#include "vfsmod.h"
|
|
|
|
|
|
|
|
static int vboxsf_dir_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(inode->i_sb);
|
|
|
|
struct shfl_createparms params = {};
|
|
|
|
struct vboxsf_dir_info *sf_d;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
sf_d = vboxsf_dir_info_alloc();
|
|
|
|
if (!sf_d)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
params.handle = SHFL_HANDLE_NIL;
|
|
|
|
params.create_flags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS |
|
|
|
|
SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;
|
|
|
|
|
|
|
|
err = vboxsf_create_at_dentry(file_dentry(file), ¶ms);
|
|
|
|
if (err)
|
|
|
|
goto err_free_dir_info;
|
|
|
|
|
|
|
|
if (params.result != SHFL_FILE_EXISTS) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto err_close;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = vboxsf_dir_read_all(sbi, sf_d, params.handle);
|
|
|
|
if (err)
|
|
|
|
goto err_close;
|
|
|
|
|
|
|
|
vboxsf_close(sbi->root, params.handle);
|
|
|
|
file->private_data = sf_d;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_close:
|
|
|
|
vboxsf_close(sbi->root, params.handle);
|
|
|
|
err_free_dir_info:
|
|
|
|
vboxsf_dir_info_free(sf_d);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vboxsf_dir_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
if (file->private_data)
|
|
|
|
vboxsf_dir_info_free(file->private_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int vboxsf_get_d_type(u32 mode)
|
|
|
|
{
|
|
|
|
unsigned int d_type;
|
|
|
|
|
|
|
|
switch (mode & SHFL_TYPE_MASK) {
|
|
|
|
case SHFL_TYPE_FIFO:
|
|
|
|
d_type = DT_FIFO;
|
|
|
|
break;
|
|
|
|
case SHFL_TYPE_DEV_CHAR:
|
|
|
|
d_type = DT_CHR;
|
|
|
|
break;
|
|
|
|
case SHFL_TYPE_DIRECTORY:
|
|
|
|
d_type = DT_DIR;
|
|
|
|
break;
|
|
|
|
case SHFL_TYPE_DEV_BLOCK:
|
|
|
|
d_type = DT_BLK;
|
|
|
|
break;
|
|
|
|
case SHFL_TYPE_FILE:
|
|
|
|
d_type = DT_REG;
|
|
|
|
break;
|
|
|
|
case SHFL_TYPE_SYMLINK:
|
|
|
|
d_type = DT_LNK;
|
|
|
|
break;
|
|
|
|
case SHFL_TYPE_SOCKET:
|
|
|
|
d_type = DT_SOCK;
|
|
|
|
break;
|
|
|
|
case SHFL_TYPE_WHITEOUT:
|
|
|
|
d_type = DT_WHT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
d_type = DT_UNKNOWN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return d_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool vboxsf_dir_emit(struct file *dir, struct dir_context *ctx)
|
|
|
|
{
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(file_inode(dir)->i_sb);
|
|
|
|
struct vboxsf_dir_info *sf_d = dir->private_data;
|
|
|
|
struct shfl_dirinfo *info;
|
|
|
|
struct vboxsf_dir_buf *b;
|
|
|
|
unsigned int d_type;
|
|
|
|
loff_t i, cur = 0;
|
|
|
|
ino_t fake_ino;
|
|
|
|
void *end;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
list_for_each_entry(b, &sf_d->info_list, head) {
|
|
|
|
try_next_entry:
|
|
|
|
if (ctx->pos >= cur + b->entries) {
|
|
|
|
cur += b->entries;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note the vboxsf_dir_info objects we are iterating over here
|
|
|
|
* are variable sized, so the info pointer may end up being
|
|
|
|
* unaligned. This is how we get the data from the host.
|
|
|
|
* Since vboxsf is only supported on x86 machines this is not
|
|
|
|
* a problem.
|
|
|
|
*/
|
|
|
|
for (i = 0, info = b->buf; i < ctx->pos - cur; i++) {
|
|
|
|
end = &info->name.string.utf8[info->name.size];
|
|
|
|
/* Only happens if the host gives us corrupt data */
|
|
|
|
if (WARN_ON(end > (b->buf + b->used)))
|
|
|
|
return false;
|
|
|
|
info = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = &info->name.string.utf8[info->name.size];
|
|
|
|
if (WARN_ON(end > (b->buf + b->used)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Info now points to the right entry, emit it. */
|
|
|
|
d_type = vboxsf_get_d_type(info->info.attr.mode);
|
|
|
|
|
|
|
|
/*
|
2020-03-14 12:13:11 +08:00
|
|
|
* On 32-bit systems pos is 64-bit signed, while ino is 32-bit
|
2019-12-12 15:09:14 +01:00
|
|
|
* unsigned so fake_ino may overflow, check for this.
|
|
|
|
*/
|
|
|
|
if ((ino_t)(ctx->pos + 1) != (u64)(ctx->pos + 1)) {
|
|
|
|
vbg_err("vboxsf: fake ino overflow, truncating dir\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
fake_ino = ctx->pos + 1;
|
|
|
|
|
|
|
|
if (sbi->nls) {
|
|
|
|
char d_name[NAME_MAX];
|
|
|
|
|
|
|
|
err = vboxsf_nlscpy(sbi, d_name, NAME_MAX,
|
|
|
|
info->name.string.utf8,
|
|
|
|
info->name.length);
|
|
|
|
if (err) {
|
|
|
|
/* skip erroneous entry and proceed */
|
|
|
|
ctx->pos += 1;
|
|
|
|
goto try_next_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dir_emit(ctx, d_name, strlen(d_name),
|
|
|
|
fake_ino, d_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dir_emit(ctx, info->name.string.utf8, info->name.length,
|
|
|
|
fake_ino, d_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vboxsf_dir_iterate(struct file *dir, struct dir_context *ctx)
|
|
|
|
{
|
|
|
|
bool emitted;
|
|
|
|
|
|
|
|
do {
|
|
|
|
emitted = vboxsf_dir_emit(dir, ctx);
|
|
|
|
if (emitted)
|
|
|
|
ctx->pos += 1;
|
|
|
|
} while (emitted);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
vfs: get rid of old '->iterate' directory operation
All users now just use '->iterate_shared()', which only takes the
directory inode lock for reading.
Filesystems that never got convered to shared mode now instead use a
wrapper that drops the lock, re-takes it in write mode, calls the old
function, and then downgrades the lock back to read mode.
This way the VFS layer and other callers no longer need to care about
filesystems that never got converted to the modern era.
The filesystems that use the new wrapper are ceph, coda, exfat, jfs,
ntfs, ocfs2, overlayfs, and vboxsf.
Honestly, several of them look like they really could just iterate their
directories in shared mode and skip the wrapper entirely, but the point
of this change is to not change semantics or fix filesystems that
haven't been fixed in the last 7+ years, but to finally get rid of the
dual iterators.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
2023-08-05 12:25:01 -07:00
|
|
|
WRAP_DIR_ITER(vboxsf_dir_iterate) // FIXME!
|
2019-12-12 15:09:14 +01:00
|
|
|
const struct file_operations vboxsf_dir_fops = {
|
|
|
|
.open = vboxsf_dir_open,
|
vfs: get rid of old '->iterate' directory operation
All users now just use '->iterate_shared()', which only takes the
directory inode lock for reading.
Filesystems that never got convered to shared mode now instead use a
wrapper that drops the lock, re-takes it in write mode, calls the old
function, and then downgrades the lock back to read mode.
This way the VFS layer and other callers no longer need to care about
filesystems that never got converted to the modern era.
The filesystems that use the new wrapper are ceph, coda, exfat, jfs,
ntfs, ocfs2, overlayfs, and vboxsf.
Honestly, several of them look like they really could just iterate their
directories in shared mode and skip the wrapper entirely, but the point
of this change is to not change semantics or fix filesystems that
haven't been fixed in the last 7+ years, but to finally get rid of the
dual iterators.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
2023-08-05 12:25:01 -07:00
|
|
|
.iterate_shared = shared_vboxsf_dir_iterate,
|
2019-12-12 15:09:14 +01:00
|
|
|
.release = vboxsf_dir_release,
|
|
|
|
.read = generic_read_dir,
|
|
|
|
.llseek = generic_file_llseek,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called during name resolution/lookup to check if the @dentry in
|
|
|
|
* the cache is still valid. the job is handled by vboxsf_inode_revalidate.
|
|
|
|
*/
|
Pass parent directory inode and expected name to ->d_revalidate()
->d_revalidate() often needs to access dentry parent and name; that has
to be done carefully, since the locking environment varies from caller
to caller. We are not guaranteed that dentry in question will not be
moved right under us - not unless the filesystem is such that nothing
on it ever gets renamed.
It can be dealt with, but that results in boilerplate code that isn't
even needed - the callers normally have just found the dentry via dcache
lookup and want to verify that it's in the right place; they already
have the values of ->d_parent and ->d_name stable. There is a couple
of exceptions (overlayfs and, to less extent, ecryptfs), but for the
majority of calls that song and dance is not needed at all.
It's easier to make ecryptfs and overlayfs find and pass those values if
there's a ->d_revalidate() instance to be called, rather than doing that
in the instances.
This commit only changes the calling conventions; making use of supplied
values is left to followups.
NOTE: some instances need more than just the parent - things like CIFS
may need to build an entire path from filesystem root, so they need
more precautions than the usual boilerplate. This series doesn't
do anything to that need - these filesystems have to keep their locking
mechanisms (rename_lock loops, use of dentry_path_raw(), private rwsem
a-la v9fs).
One thing to keep in mind when using name is that name->name will normally
point into the pathname being resolved; the filename in question occupies
name->len bytes starting at name->name, and there is NUL somewhere after it,
but it the next byte might very well be '/' rather than '\0'. Do not
ignore name->len.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Gabriel Krisman Bertazi <gabriel@krisman.be>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2024-12-08 00:28:51 -05:00
|
|
|
static int vboxsf_dentry_revalidate(struct inode *dir, const struct qstr *name,
|
|
|
|
struct dentry *dentry, unsigned int flags)
|
2019-12-12 15:09:14 +01:00
|
|
|
{
|
|
|
|
if (flags & LOOKUP_RCU)
|
|
|
|
return -ECHILD;
|
|
|
|
|
|
|
|
if (d_really_is_positive(dentry))
|
|
|
|
return vboxsf_inode_revalidate(dentry) == 0;
|
|
|
|
else
|
|
|
|
return vboxsf_stat_dentry(dentry, NULL) == -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct dentry_operations vboxsf_dentry_ops = {
|
|
|
|
.d_revalidate = vboxsf_dentry_revalidate
|
|
|
|
};
|
|
|
|
|
|
|
|
/* iops */
|
|
|
|
|
|
|
|
static struct dentry *vboxsf_dir_lookup(struct inode *parent,
|
|
|
|
struct dentry *dentry,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
|
|
|
|
struct shfl_fsobjinfo fsinfo;
|
|
|
|
struct inode *inode;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
dentry->d_time = jiffies;
|
|
|
|
|
|
|
|
err = vboxsf_stat_dentry(dentry, &fsinfo);
|
|
|
|
if (err) {
|
|
|
|
inode = (err == -ENOENT) ? NULL : ERR_PTR(err);
|
|
|
|
} else {
|
|
|
|
inode = vboxsf_new_inode(parent->i_sb);
|
|
|
|
if (!IS_ERR(inode))
|
2021-02-14 00:12:23 -05:00
|
|
|
vboxsf_init_inode(sbi, inode, &fsinfo, false);
|
2019-12-12 15:09:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return d_splice_alias(inode, dentry);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vboxsf_dir_instantiate(struct inode *parent, struct dentry *dentry,
|
|
|
|
struct shfl_fsobjinfo *info)
|
|
|
|
{
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
|
|
|
|
struct vboxsf_inode *sf_i;
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
inode = vboxsf_new_inode(parent->i_sb);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
return PTR_ERR(inode);
|
|
|
|
|
|
|
|
sf_i = VBOXSF_I(inode);
|
|
|
|
/* The host may have given us different attr then requested */
|
|
|
|
sf_i->force_restat = 1;
|
2021-02-14 00:12:23 -05:00
|
|
|
vboxsf_init_inode(sbi, inode, info, false);
|
2019-12-12 15:09:14 +01:00
|
|
|
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vboxsf_dir_create(struct inode *parent, struct dentry *dentry,
|
2021-01-21 10:22:27 +01:00
|
|
|
umode_t mode, bool is_dir, bool excl, u64 *handle_ret)
|
2019-12-12 15:09:14 +01:00
|
|
|
{
|
|
|
|
struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent);
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
|
|
|
|
struct shfl_createparms params = {};
|
|
|
|
int err;
|
|
|
|
|
|
|
|
params.handle = SHFL_HANDLE_NIL;
|
2021-01-21 10:08:59 +01:00
|
|
|
params.create_flags = SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACCESS_READWRITE;
|
|
|
|
if (is_dir)
|
|
|
|
params.create_flags |= SHFL_CF_DIRECTORY;
|
|
|
|
if (excl)
|
|
|
|
params.create_flags |= SHFL_CF_ACT_FAIL_IF_EXISTS;
|
|
|
|
|
2019-12-12 15:09:14 +01:00
|
|
|
params.info.attr.mode = (mode & 0777) |
|
|
|
|
(is_dir ? SHFL_TYPE_DIRECTORY : SHFL_TYPE_FILE);
|
|
|
|
params.info.attr.additional = SHFLFSOBJATTRADD_NOTHING;
|
|
|
|
|
|
|
|
err = vboxsf_create_at_dentry(dentry, ¶ms);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (params.result != SHFL_FILE_CREATED)
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
err = vboxsf_dir_instantiate(parent, dentry, ¶ms.info);
|
|
|
|
if (err)
|
2021-01-21 10:22:27 +01:00
|
|
|
goto out;
|
2019-12-12 15:09:14 +01:00
|
|
|
|
|
|
|
/* parent directory access/change time changed */
|
|
|
|
sf_parent_i->force_restat = 1;
|
|
|
|
|
2021-01-21 10:22:27 +01:00
|
|
|
out:
|
|
|
|
if (err == 0 && handle_ret)
|
|
|
|
*handle_ret = params.handle;
|
|
|
|
else
|
|
|
|
vboxsf_close(sbi->root, params.handle);
|
|
|
|
|
|
|
|
return err;
|
2019-12-12 15:09:14 +01:00
|
|
|
}
|
|
|
|
|
2023-01-13 12:49:13 +01:00
|
|
|
static int vboxsf_dir_mkfile(struct mnt_idmap *idmap,
|
2021-01-21 14:19:43 +01:00
|
|
|
struct inode *parent, struct dentry *dentry,
|
2019-12-12 15:09:14 +01:00
|
|
|
umode_t mode, bool excl)
|
|
|
|
{
|
2021-01-21 10:22:27 +01:00
|
|
|
return vboxsf_dir_create(parent, dentry, mode, false, excl, NULL);
|
2019-12-12 15:09:14 +01:00
|
|
|
}
|
|
|
|
|
Change inode_operations.mkdir to return struct dentry *
Some filesystems, such as NFS, cifs, ceph, and fuse, do not have
complete control of sequencing on the actual filesystem (e.g. on a
different server) and may find that the inode created for a mkdir
request already exists in the icache and dcache by the time the mkdir
request returns. For example, if the filesystem is mounted twice the
directory could be visible on the other mount before it is on the
original mount, and a pair of name_to_handle_at(), open_by_handle_at()
calls could instantiate the directory inode with an IS_ROOT() dentry
before the first mkdir returns.
This means that the dentry passed to ->mkdir() may not be the one that
is associated with the inode after the ->mkdir() completes. Some
callers need to interact with the inode after the ->mkdir completes and
they currently need to perform a lookup in the (rare) case that the
dentry is no longer hashed.
This lookup-after-mkdir requires that the directory remains locked to
avoid races. Planned future patches to lock the dentry rather than the
directory will mean that this lookup cannot be performed atomically with
the mkdir.
To remove this barrier, this patch changes ->mkdir to return the
resulting dentry if it is different from the one passed in.
Possible returns are:
NULL - the directory was created and no other dentry was used
ERR_PTR() - an error occurred
non-NULL - this other dentry was spliced in
This patch only changes file-systems to return "ERR_PTR(err)" instead of
"err" or equivalent transformations. Subsequent patches will make
further changes to some file-systems to return a correct dentry.
Not all filesystems reliably result in a positive hashed dentry:
- NFS, cifs, hostfs will sometimes need to perform a lookup of
the name to get inode information. Races could result in this
returning something different. Note that this lookup is
non-atomic which is what we are trying to avoid. Placing the
lookup in filesystem code means it only happens when the filesystem
has no other option.
- kernfs and tracefs leave the dentry negative and the ->revalidate
operation ensures that lookup will be called to correctly populate
the dentry. This could be fixed but I don't think it is important
to any of the users of vfs_mkdir() which look at the dentry.
The recommendation to use
d_drop();d_splice_alias()
is ugly but fits with current practice. A planned future patch will
change this.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: NeilBrown <neilb@suse.de>
Link: https://lore.kernel.org/r/20250227013949.536172-2-neilb@suse.de
Signed-off-by: Christian Brauner <brauner@kernel.org>
2025-02-27 12:32:53 +11:00
|
|
|
static struct dentry *vboxsf_dir_mkdir(struct mnt_idmap *idmap,
|
|
|
|
struct inode *parent, struct dentry *dentry,
|
|
|
|
umode_t mode)
|
2019-12-12 15:09:14 +01:00
|
|
|
{
|
Change inode_operations.mkdir to return struct dentry *
Some filesystems, such as NFS, cifs, ceph, and fuse, do not have
complete control of sequencing on the actual filesystem (e.g. on a
different server) and may find that the inode created for a mkdir
request already exists in the icache and dcache by the time the mkdir
request returns. For example, if the filesystem is mounted twice the
directory could be visible on the other mount before it is on the
original mount, and a pair of name_to_handle_at(), open_by_handle_at()
calls could instantiate the directory inode with an IS_ROOT() dentry
before the first mkdir returns.
This means that the dentry passed to ->mkdir() may not be the one that
is associated with the inode after the ->mkdir() completes. Some
callers need to interact with the inode after the ->mkdir completes and
they currently need to perform a lookup in the (rare) case that the
dentry is no longer hashed.
This lookup-after-mkdir requires that the directory remains locked to
avoid races. Planned future patches to lock the dentry rather than the
directory will mean that this lookup cannot be performed atomically with
the mkdir.
To remove this barrier, this patch changes ->mkdir to return the
resulting dentry if it is different from the one passed in.
Possible returns are:
NULL - the directory was created and no other dentry was used
ERR_PTR() - an error occurred
non-NULL - this other dentry was spliced in
This patch only changes file-systems to return "ERR_PTR(err)" instead of
"err" or equivalent transformations. Subsequent patches will make
further changes to some file-systems to return a correct dentry.
Not all filesystems reliably result in a positive hashed dentry:
- NFS, cifs, hostfs will sometimes need to perform a lookup of
the name to get inode information. Races could result in this
returning something different. Note that this lookup is
non-atomic which is what we are trying to avoid. Placing the
lookup in filesystem code means it only happens when the filesystem
has no other option.
- kernfs and tracefs leave the dentry negative and the ->revalidate
operation ensures that lookup will be called to correctly populate
the dentry. This could be fixed but I don't think it is important
to any of the users of vfs_mkdir() which look at the dentry.
The recommendation to use
d_drop();d_splice_alias()
is ugly but fits with current practice. A planned future patch will
change this.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: NeilBrown <neilb@suse.de>
Link: https://lore.kernel.org/r/20250227013949.536172-2-neilb@suse.de
Signed-off-by: Christian Brauner <brauner@kernel.org>
2025-02-27 12:32:53 +11:00
|
|
|
return ERR_PTR(vboxsf_dir_create(parent, dentry, mode, true, true, NULL));
|
2019-12-12 15:09:14 +01:00
|
|
|
}
|
|
|
|
|
2021-01-21 12:54:18 +01:00
|
|
|
static int vboxsf_dir_atomic_open(struct inode *parent, struct dentry *dentry,
|
|
|
|
struct file *file, unsigned int flags, umode_t mode)
|
|
|
|
{
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
|
|
|
|
struct vboxsf_handle *sf_handle;
|
|
|
|
struct dentry *res = NULL;
|
|
|
|
u64 handle;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (d_in_lookup(dentry)) {
|
|
|
|
res = vboxsf_dir_lookup(parent, dentry, 0);
|
|
|
|
if (IS_ERR(res))
|
|
|
|
return PTR_ERR(res);
|
|
|
|
|
|
|
|
if (res)
|
|
|
|
dentry = res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only creates */
|
|
|
|
if (!(flags & O_CREAT) || d_really_is_positive(dentry))
|
|
|
|
return finish_no_open(file, res);
|
|
|
|
|
|
|
|
err = vboxsf_dir_create(parent, dentry, mode, false, flags & O_EXCL, &handle);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
sf_handle = vboxsf_create_sf_handle(d_inode(dentry), handle, SHFL_CF_ACCESS_READWRITE);
|
|
|
|
if (IS_ERR(sf_handle)) {
|
|
|
|
vboxsf_close(sbi->root, handle);
|
|
|
|
err = PTR_ERR(sf_handle);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = finish_open(file, dentry, generic_file_open);
|
|
|
|
if (err) {
|
|
|
|
/* This also closes the handle passed to vboxsf_create_sf_handle() */
|
|
|
|
vboxsf_release_sf_handle(d_inode(dentry), sf_handle);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->private_data = sf_handle;
|
|
|
|
file->f_mode |= FMODE_CREATED;
|
|
|
|
out:
|
|
|
|
dput(res);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-12-12 15:09:14 +01:00
|
|
|
static int vboxsf_dir_unlink(struct inode *parent, struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
|
|
|
|
struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent);
|
|
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
struct shfl_string *path;
|
|
|
|
u32 flags;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
flags = SHFL_REMOVE_DIR;
|
|
|
|
else
|
|
|
|
flags = SHFL_REMOVE_FILE;
|
|
|
|
|
|
|
|
if (S_ISLNK(inode->i_mode))
|
|
|
|
flags |= SHFL_REMOVE_SYMLINK;
|
|
|
|
|
|
|
|
path = vboxsf_path_from_dentry(sbi, dentry);
|
|
|
|
if (IS_ERR(path))
|
|
|
|
return PTR_ERR(path);
|
|
|
|
|
|
|
|
err = vboxsf_remove(sbi->root, path, flags);
|
|
|
|
__putname(path);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* parent directory access/change time changed */
|
|
|
|
sf_parent_i->force_restat = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-01-13 12:49:17 +01:00
|
|
|
static int vboxsf_dir_rename(struct mnt_idmap *idmap,
|
2021-01-21 14:19:43 +01:00
|
|
|
struct inode *old_parent,
|
2019-12-12 15:09:14 +01:00
|
|
|
struct dentry *old_dentry,
|
|
|
|
struct inode *new_parent,
|
|
|
|
struct dentry *new_dentry,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(old_parent->i_sb);
|
|
|
|
struct vboxsf_inode *sf_old_parent_i = VBOXSF_I(old_parent);
|
|
|
|
struct vboxsf_inode *sf_new_parent_i = VBOXSF_I(new_parent);
|
|
|
|
u32 shfl_flags = SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS;
|
|
|
|
struct shfl_string *old_path, *new_path;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
old_path = vboxsf_path_from_dentry(sbi, old_dentry);
|
|
|
|
if (IS_ERR(old_path))
|
|
|
|
return PTR_ERR(old_path);
|
|
|
|
|
|
|
|
new_path = vboxsf_path_from_dentry(sbi, new_dentry);
|
|
|
|
if (IS_ERR(new_path)) {
|
|
|
|
err = PTR_ERR(new_path);
|
|
|
|
goto err_put_old_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d_inode(old_dentry)->i_mode & S_IFDIR)
|
|
|
|
shfl_flags = 0;
|
|
|
|
|
|
|
|
err = vboxsf_rename(sbi->root, old_path, new_path, shfl_flags);
|
|
|
|
if (err == 0) {
|
|
|
|
/* parent directories access/change time changed */
|
|
|
|
sf_new_parent_i->force_restat = 1;
|
|
|
|
sf_old_parent_i->force_restat = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
__putname(new_path);
|
|
|
|
err_put_old_path:
|
|
|
|
__putname(old_path);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2023-01-13 12:49:14 +01:00
|
|
|
static int vboxsf_dir_symlink(struct mnt_idmap *idmap,
|
2021-01-21 14:19:43 +01:00
|
|
|
struct inode *parent, struct dentry *dentry,
|
2019-12-12 15:09:14 +01:00
|
|
|
const char *symname)
|
|
|
|
{
|
|
|
|
struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent);
|
|
|
|
struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);
|
|
|
|
int symname_size = strlen(symname) + 1;
|
|
|
|
struct shfl_string *path, *ssymname;
|
|
|
|
struct shfl_fsobjinfo info;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
path = vboxsf_path_from_dentry(sbi, dentry);
|
|
|
|
if (IS_ERR(path))
|
|
|
|
return PTR_ERR(path);
|
|
|
|
|
|
|
|
ssymname = kmalloc(SHFLSTRING_HEADER_SIZE + symname_size, GFP_KERNEL);
|
|
|
|
if (!ssymname) {
|
|
|
|
__putname(path);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
ssymname->length = symname_size - 1;
|
|
|
|
ssymname->size = symname_size;
|
|
|
|
memcpy(ssymname->string.utf8, symname, symname_size);
|
|
|
|
|
|
|
|
err = vboxsf_symlink(sbi->root, path, ssymname, &info);
|
|
|
|
kfree(ssymname);
|
|
|
|
__putname(path);
|
|
|
|
if (err) {
|
|
|
|
/* -EROFS means symlinks are note support -> -EPERM */
|
|
|
|
return (err == -EROFS) ? -EPERM : err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = vboxsf_dir_instantiate(parent, dentry, &info);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* parent directory access/change time changed */
|
|
|
|
sf_parent_i->force_restat = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct inode_operations vboxsf_dir_iops = {
|
|
|
|
.lookup = vboxsf_dir_lookup,
|
|
|
|
.create = vboxsf_dir_mkfile,
|
|
|
|
.mkdir = vboxsf_dir_mkdir,
|
2021-01-21 12:54:18 +01:00
|
|
|
.atomic_open = vboxsf_dir_atomic_open,
|
2019-12-12 15:09:14 +01:00
|
|
|
.rmdir = vboxsf_dir_unlink,
|
|
|
|
.unlink = vboxsf_dir_unlink,
|
|
|
|
.rename = vboxsf_dir_rename,
|
|
|
|
.symlink = vboxsf_dir_symlink,
|
|
|
|
.getattr = vboxsf_getattr,
|
|
|
|
.setattr = vboxsf_setattr,
|
|
|
|
};
|