at v6.19 481 lines 12 kB view raw
1// SPDX-License-Identifier: MIT 2/* 3 * VirtualBox Guest Shared Folders support: Directory inode and file operations 4 * 5 * Copyright (C) 2006-2018 Oracle Corporation 6 */ 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), &params); 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 here 116 * are variable sized, so the info pointer may end up being 117 * unaligned. This is how we get the data from the host. 118 * Since vboxsf is only supported on x86 machines this is not 119 * 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-bit 138 * 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 .setlease = simple_nosetlease, 190}; 191 192/* 193 * This is called during name resolution/lookup to check if the @dentry in 194 * the cache is still valid. the job is handled by vboxsf_inode_revalidate. 195 */ 196static int vboxsf_dentry_revalidate(struct inode *dir, const struct qstr *name, 197 struct dentry *dentry, unsigned int flags) 198{ 199 if (flags & LOOKUP_RCU) 200 return -ECHILD; 201 202 if (d_really_is_positive(dentry)) 203 return vboxsf_inode_revalidate(dentry) == 0; 204 else 205 return vboxsf_stat_dentry(dentry, NULL) == -ENOENT; 206} 207 208const struct dentry_operations vboxsf_dentry_ops = { 209 .d_revalidate = vboxsf_dentry_revalidate 210}; 211 212/* iops */ 213 214static struct dentry *vboxsf_dir_lookup(struct inode *parent, 215 struct dentry *dentry, 216 unsigned int flags) 217{ 218 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb); 219 struct shfl_fsobjinfo fsinfo; 220 struct inode *inode; 221 int err; 222 223 dentry->d_time = jiffies; 224 225 err = vboxsf_stat_dentry(dentry, &fsinfo); 226 if (err) { 227 inode = (err == -ENOENT) ? NULL : ERR_PTR(err); 228 } else { 229 inode = vboxsf_new_inode(parent->i_sb); 230 if (!IS_ERR(inode)) 231 vboxsf_init_inode(sbi, inode, &fsinfo, false); 232 } 233 234 return d_splice_alias(inode, dentry); 235} 236 237static int vboxsf_dir_instantiate(struct inode *parent, struct dentry *dentry, 238 struct shfl_fsobjinfo *info) 239{ 240 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb); 241 struct vboxsf_inode *sf_i; 242 struct inode *inode; 243 244 inode = vboxsf_new_inode(parent->i_sb); 245 if (IS_ERR(inode)) 246 return PTR_ERR(inode); 247 248 sf_i = VBOXSF_I(inode); 249 /* The host may have given us different attr then requested */ 250 sf_i->force_restat = 1; 251 vboxsf_init_inode(sbi, inode, info, false); 252 253 d_instantiate(dentry, inode); 254 255 return 0; 256} 257 258static int vboxsf_dir_create(struct inode *parent, struct dentry *dentry, 259 umode_t mode, bool is_dir, bool excl, u64 *handle_ret) 260{ 261 struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent); 262 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb); 263 struct shfl_createparms params = {}; 264 int err; 265 266 params.handle = SHFL_HANDLE_NIL; 267 params.create_flags = SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACCESS_READWRITE; 268 if (is_dir) 269 params.create_flags |= SHFL_CF_DIRECTORY; 270 if (excl) 271 params.create_flags |= SHFL_CF_ACT_FAIL_IF_EXISTS; 272 273 params.info.attr.mode = (mode & 0777) | 274 (is_dir ? SHFL_TYPE_DIRECTORY : SHFL_TYPE_FILE); 275 params.info.attr.additional = SHFLFSOBJATTRADD_NOTHING; 276 277 err = vboxsf_create_at_dentry(dentry, &params); 278 if (err) 279 return err; 280 281 if (params.result != SHFL_FILE_CREATED) 282 return -EPERM; 283 284 err = vboxsf_dir_instantiate(parent, dentry, &params.info); 285 if (err) 286 goto out; 287 288 /* parent directory access/change time changed */ 289 sf_parent_i->force_restat = 1; 290 291out: 292 if (err == 0 && handle_ret) 293 *handle_ret = params.handle; 294 else 295 vboxsf_close(sbi->root, params.handle); 296 297 return err; 298} 299 300static int vboxsf_dir_mkfile(struct mnt_idmap *idmap, 301 struct inode *parent, struct dentry *dentry, 302 umode_t mode, bool excl) 303{ 304 return vboxsf_dir_create(parent, dentry, mode, false, excl, NULL); 305} 306 307static struct dentry *vboxsf_dir_mkdir(struct mnt_idmap *idmap, 308 struct inode *parent, struct dentry *dentry, 309 umode_t mode) 310{ 311 return ERR_PTR(vboxsf_dir_create(parent, dentry, mode, true, true, NULL)); 312} 313 314static int vboxsf_dir_atomic_open(struct inode *parent, struct dentry *dentry, 315 struct file *file, unsigned int flags, umode_t mode) 316{ 317 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb); 318 struct vboxsf_handle *sf_handle; 319 u64 handle; 320 int err; 321 322 if (d_in_lookup(dentry)) { 323 struct dentry *res = vboxsf_dir_lookup(parent, dentry, 0); 324 if (res || d_really_is_positive(dentry)) 325 return finish_no_open(file, res); 326 } 327 328 /* Only creates */ 329 if (!(flags & O_CREAT)) 330 return finish_no_open(file, NULL); 331 332 err = vboxsf_dir_create(parent, dentry, mode, false, flags & O_EXCL, &handle); 333 if (err) 334 return err; 335 336 sf_handle = vboxsf_create_sf_handle(d_inode(dentry), handle, SHFL_CF_ACCESS_READWRITE); 337 if (IS_ERR(sf_handle)) { 338 vboxsf_close(sbi->root, handle); 339 return PTR_ERR(sf_handle); 340 } 341 342 err = finish_open(file, dentry, generic_file_open); 343 if (err) { 344 /* This also closes the handle passed to vboxsf_create_sf_handle() */ 345 vboxsf_release_sf_handle(d_inode(dentry), sf_handle); 346 return err; 347 } 348 349 file->private_data = sf_handle; 350 file->f_mode |= FMODE_CREATED; 351 return 0; 352} 353 354static int vboxsf_dir_unlink(struct inode *parent, struct dentry *dentry) 355{ 356 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb); 357 struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent); 358 struct inode *inode = d_inode(dentry); 359 struct shfl_string *path; 360 u32 flags; 361 int err; 362 363 if (S_ISDIR(inode->i_mode)) 364 flags = SHFL_REMOVE_DIR; 365 else 366 flags = SHFL_REMOVE_FILE; 367 368 if (S_ISLNK(inode->i_mode)) 369 flags |= SHFL_REMOVE_SYMLINK; 370 371 path = vboxsf_path_from_dentry(sbi, dentry); 372 if (IS_ERR(path)) 373 return PTR_ERR(path); 374 375 err = vboxsf_remove(sbi->root, path, flags); 376 __putname(path); 377 if (err) 378 return err; 379 380 /* parent directory access/change time changed */ 381 sf_parent_i->force_restat = 1; 382 383 return 0; 384} 385 386static int vboxsf_dir_rename(struct mnt_idmap *idmap, 387 struct inode *old_parent, 388 struct dentry *old_dentry, 389 struct inode *new_parent, 390 struct dentry *new_dentry, 391 unsigned int flags) 392{ 393 struct vboxsf_sbi *sbi = VBOXSF_SBI(old_parent->i_sb); 394 struct vboxsf_inode *sf_old_parent_i = VBOXSF_I(old_parent); 395 struct vboxsf_inode *sf_new_parent_i = VBOXSF_I(new_parent); 396 u32 shfl_flags = SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS; 397 struct shfl_string *old_path, *new_path; 398 int err; 399 400 if (flags) 401 return -EINVAL; 402 403 old_path = vboxsf_path_from_dentry(sbi, old_dentry); 404 if (IS_ERR(old_path)) 405 return PTR_ERR(old_path); 406 407 new_path = vboxsf_path_from_dentry(sbi, new_dentry); 408 if (IS_ERR(new_path)) { 409 err = PTR_ERR(new_path); 410 goto err_put_old_path; 411 } 412 413 if (d_inode(old_dentry)->i_mode & S_IFDIR) 414 shfl_flags = 0; 415 416 err = vboxsf_rename(sbi->root, old_path, new_path, shfl_flags); 417 if (err == 0) { 418 /* parent directories access/change time changed */ 419 sf_new_parent_i->force_restat = 1; 420 sf_old_parent_i->force_restat = 1; 421 } 422 423 __putname(new_path); 424err_put_old_path: 425 __putname(old_path); 426 return err; 427} 428 429static int vboxsf_dir_symlink(struct mnt_idmap *idmap, 430 struct inode *parent, struct dentry *dentry, 431 const char *symname) 432{ 433 struct vboxsf_inode *sf_parent_i = VBOXSF_I(parent); 434 struct vboxsf_sbi *sbi = VBOXSF_SBI(parent->i_sb); 435 int symname_size = strlen(symname) + 1; 436 struct shfl_string *path, *ssymname; 437 struct shfl_fsobjinfo info; 438 int err; 439 440 path = vboxsf_path_from_dentry(sbi, dentry); 441 if (IS_ERR(path)) 442 return PTR_ERR(path); 443 444 ssymname = kmalloc(SHFLSTRING_HEADER_SIZE + symname_size, GFP_KERNEL); 445 if (!ssymname) { 446 __putname(path); 447 return -ENOMEM; 448 } 449 ssymname->length = symname_size - 1; 450 ssymname->size = symname_size; 451 memcpy(ssymname->string.utf8, symname, symname_size); 452 453 err = vboxsf_symlink(sbi->root, path, ssymname, &info); 454 kfree(ssymname); 455 __putname(path); 456 if (err) { 457 /* -EROFS means symlinks are note support -> -EPERM */ 458 return (err == -EROFS) ? -EPERM : err; 459 } 460 461 err = vboxsf_dir_instantiate(parent, dentry, &info); 462 if (err) 463 return err; 464 465 /* parent directory access/change time changed */ 466 sf_parent_i->force_restat = 1; 467 return 0; 468} 469 470const struct inode_operations vboxsf_dir_iops = { 471 .lookup = vboxsf_dir_lookup, 472 .create = vboxsf_dir_mkfile, 473 .mkdir = vboxsf_dir_mkdir, 474 .atomic_open = vboxsf_dir_atomic_open, 475 .rmdir = vboxsf_dir_unlink, 476 .unlink = vboxsf_dir_unlink, 477 .rename = vboxsf_dir_rename, 478 .symlink = vboxsf_dir_symlink, 479 .getattr = vboxsf_getattr, 480 .setattr = vboxsf_setattr, 481};