487 lines · c
1// SPDX-License-Identifier: MIT2/*3 * VirtualBox Guest Shared Folders support: Directory inode and file operations4 *5 * Copyright (C) 2006-2018 Oracle Corporation6 */7 8#include <linux/namei.h>9#include <linux/vbox_utils.h>10#include "vfsmod.h"11 12static int vboxsf_dir_open(struct inode *inode, struct file *file)13{14 struct vboxsf_sbi *sbi = VBOXSF_SBI(inode->i_sb);15 struct shfl_createparms params = {};16 struct vboxsf_dir_info *sf_d;17 int err;18 19 sf_d = vboxsf_dir_info_alloc();20 if (!sf_d)21 return -ENOMEM;22 23 params.handle = SHFL_HANDLE_NIL;24 params.create_flags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS |25 SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;26 27 err = vboxsf_create_at_dentry(file_dentry(file), ¶ms);28 if (err)29 goto err_free_dir_info;30 31 if (params.result != SHFL_FILE_EXISTS) {32 err = -ENOENT;33 goto err_close;34 }35 36 err = vboxsf_dir_read_all(sbi, sf_d, params.handle);37 if (err)38 goto err_close;39 40 vboxsf_close(sbi->root, params.handle);41 file->private_data = sf_d;42 return 0;43 44err_close:45 vboxsf_close(sbi->root, params.handle);46err_free_dir_info:47 vboxsf_dir_info_free(sf_d);48 return err;49}50 51static int vboxsf_dir_release(struct inode *inode, struct file *file)52{53 if (file->private_data)54 vboxsf_dir_info_free(file->private_data);55 56 return 0;57}58 59static unsigned int vboxsf_get_d_type(u32 mode)60{61 unsigned int d_type;62 63 switch (mode & SHFL_TYPE_MASK) {64 case SHFL_TYPE_FIFO:65 d_type = DT_FIFO;66 break;67 case SHFL_TYPE_DEV_CHAR:68 d_type = DT_CHR;69 break;70 case SHFL_TYPE_DIRECTORY:71 d_type = DT_DIR;72 break;73 case SHFL_TYPE_DEV_BLOCK:74 d_type = DT_BLK;75 break;76 case SHFL_TYPE_FILE:77 d_type = DT_REG;78 break;79 case SHFL_TYPE_SYMLINK:80 d_type = DT_LNK;81 break;82 case SHFL_TYPE_SOCKET:83 d_type = DT_SOCK;84 break;85 case SHFL_TYPE_WHITEOUT:86 d_type = DT_WHT;87 break;88 default:89 d_type = DT_UNKNOWN;90 break;91 }92 return d_type;93}94 95static bool vboxsf_dir_emit(struct file *dir, struct dir_context *ctx)96{97 struct vboxsf_sbi *sbi = VBOXSF_SBI(file_inode(dir)->i_sb);98 struct vboxsf_dir_info *sf_d = dir->private_data;99 struct shfl_dirinfo *info;100 struct vboxsf_dir_buf *b;101 unsigned int d_type;102 loff_t i, cur = 0;103 ino_t fake_ino;104 void *end;105 int err;106 107 list_for_each_entry(b, &sf_d->info_list, head) {108try_next_entry:109 if (ctx->pos >= cur + b->entries) {110 cur += b->entries;111 continue;112 }113 114 /*115 * Note the vboxsf_dir_info objects we are iterating over here116 * are variable sized, so the info pointer may end up being117 * unaligned. This is how we get the data from the host.118 * Since vboxsf is only supported on x86 machines this is not119 * a problem.120 */121 for (i = 0, info = b->buf; i < ctx->pos - cur; i++) {122 end = &info->name.string.utf8[info->name.size];123 /* Only happens if the host gives us corrupt data */124 if (WARN_ON(end > (b->buf + b->used)))125 return false;126 info = end;127 }128 129 end = &info->name.string.utf8[info->name.size];130 if (WARN_ON(end > (b->buf + b->used)))131 return false;132 133 /* Info now points to the right entry, emit it. */134 d_type = vboxsf_get_d_type(info->info.attr.mode);135 136 /*137 * On 32-bit systems pos is 64-bit signed, while ino is 32-bit138 * unsigned so fake_ino may overflow, check for this.139 */140 if ((ino_t)(ctx->pos + 1) != (u64)(ctx->pos + 1)) {141 vbg_err("vboxsf: fake ino overflow, truncating dir\n");142 return false;143 }144 fake_ino = ctx->pos + 1;145 146 if (sbi->nls) {147 char d_name[NAME_MAX];148 149 err = vboxsf_nlscpy(sbi, d_name, NAME_MAX,150 info->name.string.utf8,151 info->name.length);152 if (err) {153 /* skip erroneous entry and proceed */154 ctx->pos += 1;155 goto try_next_entry;156 }157 158 return dir_emit(ctx, d_name, strlen(d_name),159 fake_ino, d_type);160 }161 162 return dir_emit(ctx, info->name.string.utf8, info->name.length,163 fake_ino, d_type);164 }165 166 return false;167}168 169static int vboxsf_dir_iterate(struct file *dir, struct dir_context *ctx)170{171 bool emitted;172 173 do {174 emitted = vboxsf_dir_emit(dir, ctx);175 if (emitted)176 ctx->pos += 1;177 } while (emitted);178 179 return 0;180}181 182WRAP_DIR_ITER(vboxsf_dir_iterate) // FIXME!183const struct file_operations vboxsf_dir_fops = {184 .open = vboxsf_dir_open,185 .iterate_shared = shared_vboxsf_dir_iterate,186 .release = vboxsf_dir_release,187 .read = generic_read_dir,188 .llseek = generic_file_llseek,189};190 191/*192 * This is called during name resolution/lookup to check if the @dentry in193 * the cache is still valid. the job is handled by vboxsf_inode_revalidate.194 */195static int vboxsf_dentry_revalidate(struct dentry *dentry, unsigned int flags)196{197 if (flags & LOOKUP_RCU)198 return -ECHILD;199 200 if (d_really_is_positive(dentry))201 return vboxsf_inode_revalidate(dentry) == 0;202 else203 return vboxsf_stat_dentry(dentry, NULL) == -ENOENT;204}205 206const struct dentry_operations vboxsf_dentry_ops = {207 .d_revalidate = vboxsf_dentry_revalidate208};209 210/* iops */211 212static struct dentry *vboxsf_dir_lookup(struct inode *parent,213 struct dentry *dentry,214 unsigned int flags)215{216 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);217 struct shfl_fsobjinfo fsinfo;218 struct inode *inode;219 int err;220 221 dentry->d_time = jiffies;222 223 err = vboxsf_stat_dentry(dentry, &fsinfo);224 if (err) {225 inode = (err == -ENOENT) ? NULL : ERR_PTR(err);226 } else {227 inode = vboxsf_new_inode(parent->i_sb);228 if (!IS_ERR(inode))229 vboxsf_init_inode(sbi, inode, &fsinfo, false);230 }231 232 return d_splice_alias(inode, dentry);233}234 235static int vboxsf_dir_instantiate(struct inode *parent, struct dentry *dentry,236 struct shfl_fsobjinfo *info)237{238 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);239 struct vboxsf_inode *sf_i;240 struct inode *inode;241 242 inode = vboxsf_new_inode(parent->i_sb);243 if (IS_ERR(inode))244 return PTR_ERR(inode);245 246 sf_i = VBOXSF_I(inode);247 /* The host may have given us different attr then requested */248 sf_i->force_restat = 1;249 vboxsf_init_inode(sbi, inode, info, false);250 251 d_instantiate(dentry, inode);252 253 return 0;254}255 256static int vboxsf_dir_create(struct inode *parent, struct dentry *dentry,257 umode_t mode, bool is_dir, bool excl, u64 *handle_ret)258{259 struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent);260 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);261 struct shfl_createparms params = {};262 int err;263 264 params.handle = SHFL_HANDLE_NIL;265 params.create_flags = SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACCESS_READWRITE;266 if (is_dir)267 params.create_flags |= SHFL_CF_DIRECTORY;268 if (excl)269 params.create_flags |= SHFL_CF_ACT_FAIL_IF_EXISTS;270 271 params.info.attr.mode = (mode & 0777) |272 (is_dir ? SHFL_TYPE_DIRECTORY : SHFL_TYPE_FILE);273 params.info.attr.additional = SHFLFSOBJATTRADD_NOTHING;274 275 err = vboxsf_create_at_dentry(dentry, ¶ms);276 if (err)277 return err;278 279 if (params.result != SHFL_FILE_CREATED)280 return -EPERM;281 282 err = vboxsf_dir_instantiate(parent, dentry, ¶ms.info);283 if (err)284 goto out;285 286 /* parent directory access/change time changed */287 sf_parent_i->force_restat = 1;288 289out:290 if (err == 0 && handle_ret)291 *handle_ret = params.handle;292 else293 vboxsf_close(sbi->root, params.handle);294 295 return err;296}297 298static int vboxsf_dir_mkfile(struct mnt_idmap *idmap,299 struct inode *parent, struct dentry *dentry,300 umode_t mode, bool excl)301{302 return vboxsf_dir_create(parent, dentry, mode, false, excl, NULL);303}304 305static int vboxsf_dir_mkdir(struct mnt_idmap *idmap,306 struct inode *parent, struct dentry *dentry,307 umode_t mode)308{309 return vboxsf_dir_create(parent, dentry, mode, true, true, NULL);310}311 312static int vboxsf_dir_atomic_open(struct inode *parent, struct dentry *dentry,313 struct file *file, unsigned int flags, umode_t mode)314{315 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);316 struct vboxsf_handle *sf_handle;317 struct dentry *res = NULL;318 u64 handle;319 int err;320 321 if (d_in_lookup(dentry)) {322 res = vboxsf_dir_lookup(parent, dentry, 0);323 if (IS_ERR(res))324 return PTR_ERR(res);325 326 if (res)327 dentry = res;328 }329 330 /* Only creates */331 if (!(flags & O_CREAT) || d_really_is_positive(dentry))332 return finish_no_open(file, res);333 334 err = vboxsf_dir_create(parent, dentry, mode, false, flags & O_EXCL, &handle);335 if (err)336 goto out;337 338 sf_handle = vboxsf_create_sf_handle(d_inode(dentry), handle, SHFL_CF_ACCESS_READWRITE);339 if (IS_ERR(sf_handle)) {340 vboxsf_close(sbi->root, handle);341 err = PTR_ERR(sf_handle);342 goto out;343 }344 345 err = finish_open(file, dentry, generic_file_open);346 if (err) {347 /* This also closes the handle passed to vboxsf_create_sf_handle() */348 vboxsf_release_sf_handle(d_inode(dentry), sf_handle);349 goto out;350 }351 352 file->private_data = sf_handle;353 file->f_mode |= FMODE_CREATED;354out:355 dput(res);356 return err;357}358 359static int vboxsf_dir_unlink(struct inode *parent, struct dentry *dentry)360{361 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);362 struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent);363 struct inode *inode = d_inode(dentry);364 struct shfl_string *path;365 u32 flags;366 int err;367 368 if (S_ISDIR(inode->i_mode))369 flags = SHFL_REMOVE_DIR;370 else371 flags = SHFL_REMOVE_FILE;372 373 if (S_ISLNK(inode->i_mode))374 flags |= SHFL_REMOVE_SYMLINK;375 376 path = vboxsf_path_from_dentry(sbi, dentry);377 if (IS_ERR(path))378 return PTR_ERR(path);379 380 err = vboxsf_remove(sbi->root, path, flags);381 __putname(path);382 if (err)383 return err;384 385 /* parent directory access/change time changed */386 sf_parent_i->force_restat = 1;387 388 return 0;389}390 391static int vboxsf_dir_rename(struct mnt_idmap *idmap,392 struct inode *old_parent,393 struct dentry *old_dentry,394 struct inode *new_parent,395 struct dentry *new_dentry,396 unsigned int flags)397{398 struct vboxsf_sbi *sbi = VBOXSF_SBI(old_parent->i_sb);399 struct vboxsf_inode *sf_old_parent_i = VBOXSF_I(old_parent);400 struct vboxsf_inode *sf_new_parent_i = VBOXSF_I(new_parent);401 u32 shfl_flags = SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS;402 struct shfl_string *old_path, *new_path;403 int err;404 405 if (flags)406 return -EINVAL;407 408 old_path = vboxsf_path_from_dentry(sbi, old_dentry);409 if (IS_ERR(old_path))410 return PTR_ERR(old_path);411 412 new_path = vboxsf_path_from_dentry(sbi, new_dentry);413 if (IS_ERR(new_path)) {414 err = PTR_ERR(new_path);415 goto err_put_old_path;416 }417 418 if (d_inode(old_dentry)->i_mode & S_IFDIR)419 shfl_flags = 0;420 421 err = vboxsf_rename(sbi->root, old_path, new_path, shfl_flags);422 if (err == 0) {423 /* parent directories access/change time changed */424 sf_new_parent_i->force_restat = 1;425 sf_old_parent_i->force_restat = 1;426 }427 428 __putname(new_path);429err_put_old_path:430 __putname(old_path);431 return err;432}433 434static int vboxsf_dir_symlink(struct mnt_idmap *idmap,435 struct inode *parent, struct dentry *dentry,436 const char *symname)437{438 struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent);439 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb);440 int symname_size = strlen(symname) + 1;441 struct shfl_string *path, *ssymname;442 struct shfl_fsobjinfo info;443 int err;444 445 path = vboxsf_path_from_dentry(sbi, dentry);446 if (IS_ERR(path))447 return PTR_ERR(path);448 449 ssymname = kmalloc(SHFLSTRING_HEADER_SIZE + symname_size, GFP_KERNEL);450 if (!ssymname) {451 __putname(path);452 return -ENOMEM;453 }454 ssymname->length = symname_size - 1;455 ssymname->size = symname_size;456 memcpy(ssymname->string.utf8, symname, symname_size);457 458 err = vboxsf_symlink(sbi->root, path, ssymname, &info);459 kfree(ssymname);460 __putname(path);461 if (err) {462 /* -EROFS means symlinks are note support -> -EPERM */463 return (err == -EROFS) ? -EPERM : err;464 }465 466 err = vboxsf_dir_instantiate(parent, dentry, &info);467 if (err)468 return err;469 470 /* parent directory access/change time changed */471 sf_parent_i->force_restat = 1;472 return 0;473}474 475const struct inode_operations vboxsf_dir_iops = {476 .lookup = vboxsf_dir_lookup,477 .create = vboxsf_dir_mkfile,478 .mkdir = vboxsf_dir_mkdir,479 .atomic_open = vboxsf_dir_atomic_open,480 .rmdir = vboxsf_dir_unlink,481 .unlink = vboxsf_dir_unlink,482 .rename = vboxsf_dir_rename,483 .symlink = vboxsf_dir_symlink,484 .getattr = vboxsf_getattr,485 .setattr = vboxsf_setattr,486};487