570 lines · c
1// SPDX-License-Identifier: MIT2/*3 * VirtualBox Guest Shared Folders support: Utility functions.4 * Mainly conversion from/to VirtualBox/Linux data structures.5 *6 * Copyright (C) 2006-2018 Oracle Corporation7 */8 9#include <linux/namei.h>10#include <linux/nls.h>11#include <linux/sizes.h>12#include <linux/pagemap.h>13#include <linux/vfs.h>14#include "vfsmod.h"15 16struct inode *vboxsf_new_inode(struct super_block *sb)17{18 struct vboxsf_sbi *sbi = VBOXSF_SBI(sb);19 struct inode *inode;20 unsigned long flags;21 int cursor, ret;22 u32 gen;23 24 inode = new_inode(sb);25 if (!inode)26 return ERR_PTR(-ENOMEM);27 28 idr_preload(GFP_KERNEL);29 spin_lock_irqsave(&sbi->ino_idr_lock, flags);30 cursor = idr_get_cursor(&sbi->ino_idr);31 ret = idr_alloc_cyclic(&sbi->ino_idr, inode, 1, 0, GFP_ATOMIC);32 if (ret >= 0 && ret < cursor)33 sbi->next_generation++;34 gen = sbi->next_generation;35 spin_unlock_irqrestore(&sbi->ino_idr_lock, flags);36 idr_preload_end();37 38 if (ret < 0) {39 iput(inode);40 return ERR_PTR(ret);41 }42 43 inode->i_ino = ret;44 inode->i_generation = gen;45 return inode;46}47 48/* set [inode] attributes based on [info], uid/gid based on [sbi] */49int vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,50 const struct shfl_fsobjinfo *info, bool reinit)51{52 const struct shfl_fsobjattr *attr;53 s64 allocated;54 umode_t mode;55 56 attr = &info->attr;57 58#define mode_set(r) ((attr->mode & (SHFL_UNIX_##r)) ? (S_##r) : 0)59 60 mode = mode_set(IRUSR);61 mode |= mode_set(IWUSR);62 mode |= mode_set(IXUSR);63 64 mode |= mode_set(IRGRP);65 mode |= mode_set(IWGRP);66 mode |= mode_set(IXGRP);67 68 mode |= mode_set(IROTH);69 mode |= mode_set(IWOTH);70 mode |= mode_set(IXOTH);71 72#undef mode_set73 74 /* We use the host-side values for these */75 inode->i_flags |= S_NOATIME | S_NOCMTIME;76 inode->i_mapping->a_ops = &vboxsf_reg_aops;77 78 if (SHFL_IS_DIRECTORY(attr->mode)) {79 if (sbi->o.dmode_set)80 mode = sbi->o.dmode;81 mode &= ~sbi->o.dmask;82 mode |= S_IFDIR;83 if (!reinit) {84 inode->i_op = &vboxsf_dir_iops;85 inode->i_fop = &vboxsf_dir_fops;86 /*87 * XXX: this probably should be set to the number of entries88 * in the directory plus two (. ..)89 */90 set_nlink(inode, 1);91 } else if (!S_ISDIR(inode->i_mode))92 return -ESTALE;93 inode->i_mode = mode;94 } else if (SHFL_IS_SYMLINK(attr->mode)) {95 if (sbi->o.fmode_set)96 mode = sbi->o.fmode;97 mode &= ~sbi->o.fmask;98 mode |= S_IFLNK;99 if (!reinit) {100 inode->i_op = &vboxsf_lnk_iops;101 set_nlink(inode, 1);102 } else if (!S_ISLNK(inode->i_mode))103 return -ESTALE;104 inode->i_mode = mode;105 } else {106 if (sbi->o.fmode_set)107 mode = sbi->o.fmode;108 mode &= ~sbi->o.fmask;109 mode |= S_IFREG;110 if (!reinit) {111 inode->i_op = &vboxsf_reg_iops;112 inode->i_fop = &vboxsf_reg_fops;113 set_nlink(inode, 1);114 } else if (!S_ISREG(inode->i_mode))115 return -ESTALE;116 inode->i_mode = mode;117 }118 119 inode->i_uid = sbi->o.uid;120 inode->i_gid = sbi->o.gid;121 122 inode->i_size = info->size;123 inode->i_blkbits = 12;124 /* i_blocks always in units of 512 bytes! */125 allocated = info->allocated + 511;126 do_div(allocated, 512);127 inode->i_blocks = allocated;128 129 inode_set_atime_to_ts(inode,130 ns_to_timespec64(info->access_time.ns_relative_to_unix_epoch));131 inode_set_ctime_to_ts(inode,132 ns_to_timespec64(info->change_time.ns_relative_to_unix_epoch));133 inode_set_mtime_to_ts(inode,134 ns_to_timespec64(info->modification_time.ns_relative_to_unix_epoch));135 return 0;136}137 138int vboxsf_create_at_dentry(struct dentry *dentry,139 struct shfl_createparms *params)140{141 struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);142 struct shfl_string *path;143 int err;144 145 path = vboxsf_path_from_dentry(sbi, dentry);146 if (IS_ERR(path))147 return PTR_ERR(path);148 149 err = vboxsf_create(sbi->root, path, params);150 __putname(path);151 152 return err;153}154 155int vboxsf_stat(struct vboxsf_sbi *sbi, struct shfl_string *path,156 struct shfl_fsobjinfo *info)157{158 struct shfl_createparms params = {};159 int err;160 161 params.handle = SHFL_HANDLE_NIL;162 params.create_flags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;163 164 err = vboxsf_create(sbi->root, path, ¶ms);165 if (err)166 return err;167 168 if (params.result != SHFL_FILE_EXISTS)169 return -ENOENT;170 171 if (info)172 *info = params.info;173 174 return 0;175}176 177int vboxsf_stat_dentry(struct dentry *dentry, struct shfl_fsobjinfo *info)178{179 struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);180 struct shfl_string *path;181 int err;182 183 path = vboxsf_path_from_dentry(sbi, dentry);184 if (IS_ERR(path))185 return PTR_ERR(path);186 187 err = vboxsf_stat(sbi, path, info);188 __putname(path);189 return err;190}191 192int vboxsf_inode_revalidate(struct dentry *dentry)193{194 struct vboxsf_sbi *sbi;195 struct vboxsf_inode *sf_i;196 struct shfl_fsobjinfo info;197 struct timespec64 mtime, prev_mtime;198 struct inode *inode;199 int err;200 201 if (!dentry || !d_really_is_positive(dentry))202 return -EINVAL;203 204 inode = d_inode(dentry);205 prev_mtime = inode_get_mtime(inode);206 sf_i = VBOXSF_I(inode);207 sbi = VBOXSF_SBI(dentry->d_sb);208 if (!sf_i->force_restat) {209 if (time_before(jiffies, dentry->d_time + sbi->o.ttl))210 return 0;211 }212 213 err = vboxsf_stat_dentry(dentry, &info);214 if (err)215 return err;216 217 dentry->d_time = jiffies;218 sf_i->force_restat = 0;219 err = vboxsf_init_inode(sbi, inode, &info, true);220 if (err)221 return err;222 223 /*224 * If the file was changed on the host side we need to invalidate the225 * page-cache for it. Note this also gets triggered by our own writes,226 * this is unavoidable.227 */228 mtime = inode_get_mtime(inode);229 if (timespec64_compare(&mtime, &prev_mtime) > 0)230 invalidate_inode_pages2(inode->i_mapping);231 232 return 0;233}234 235int vboxsf_getattr(struct mnt_idmap *idmap, const struct path *path,236 struct kstat *kstat, u32 request_mask, unsigned int flags)237{238 int err;239 struct dentry *dentry = path->dentry;240 struct inode *inode = d_inode(dentry);241 struct vboxsf_inode *sf_i = VBOXSF_I(inode);242 243 switch (flags & AT_STATX_SYNC_TYPE) {244 case AT_STATX_DONT_SYNC:245 err = 0;246 break;247 case AT_STATX_FORCE_SYNC:248 sf_i->force_restat = 1;249 fallthrough;250 default:251 err = vboxsf_inode_revalidate(dentry);252 }253 if (err)254 return err;255 256 generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(dentry), kstat);257 return 0;258}259 260int vboxsf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,261 struct iattr *iattr)262{263 struct vboxsf_inode *sf_i = VBOXSF_I(d_inode(dentry));264 struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);265 struct shfl_createparms params = {};266 struct shfl_fsobjinfo info = {};267 u32 buf_len;268 int err;269 270 params.handle = SHFL_HANDLE_NIL;271 params.create_flags = SHFL_CF_ACT_OPEN_IF_EXISTS |272 SHFL_CF_ACT_FAIL_IF_NEW |273 SHFL_CF_ACCESS_ATTR_WRITE;274 275 /* this is at least required for Posix hosts */276 if (iattr->ia_valid & ATTR_SIZE)277 params.create_flags |= SHFL_CF_ACCESS_WRITE;278 279 err = vboxsf_create_at_dentry(dentry, ¶ms);280 if (err || params.result != SHFL_FILE_EXISTS)281 return err ? err : -ENOENT;282 283#define mode_set(r) ((iattr->ia_mode & (S_##r)) ? SHFL_UNIX_##r : 0)284 285 /*286 * Setting the file size and setting the other attributes has to287 * be handled separately.288 */289 if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) {290 if (iattr->ia_valid & ATTR_MODE) {291 info.attr.mode = mode_set(IRUSR);292 info.attr.mode |= mode_set(IWUSR);293 info.attr.mode |= mode_set(IXUSR);294 info.attr.mode |= mode_set(IRGRP);295 info.attr.mode |= mode_set(IWGRP);296 info.attr.mode |= mode_set(IXGRP);297 info.attr.mode |= mode_set(IROTH);298 info.attr.mode |= mode_set(IWOTH);299 info.attr.mode |= mode_set(IXOTH);300 301 if (iattr->ia_mode & S_IFDIR)302 info.attr.mode |= SHFL_TYPE_DIRECTORY;303 else304 info.attr.mode |= SHFL_TYPE_FILE;305 }306 307 if (iattr->ia_valid & ATTR_ATIME)308 info.access_time.ns_relative_to_unix_epoch =309 timespec64_to_ns(&iattr->ia_atime);310 311 if (iattr->ia_valid & ATTR_MTIME)312 info.modification_time.ns_relative_to_unix_epoch =313 timespec64_to_ns(&iattr->ia_mtime);314 315 /*316 * Ignore ctime (inode change time) as it can't be set317 * from userland anyway.318 */319 320 buf_len = sizeof(info);321 err = vboxsf_fsinfo(sbi->root, params.handle,322 SHFL_INFO_SET | SHFL_INFO_FILE, &buf_len,323 &info);324 if (err) {325 vboxsf_close(sbi->root, params.handle);326 return err;327 }328 329 /* the host may have given us different attr then requested */330 sf_i->force_restat = 1;331 }332 333#undef mode_set334 335 if (iattr->ia_valid & ATTR_SIZE) {336 memset(&info, 0, sizeof(info));337 info.size = iattr->ia_size;338 buf_len = sizeof(info);339 err = vboxsf_fsinfo(sbi->root, params.handle,340 SHFL_INFO_SET | SHFL_INFO_SIZE, &buf_len,341 &info);342 if (err) {343 vboxsf_close(sbi->root, params.handle);344 return err;345 }346 347 /* the host may have given us different attr then requested */348 sf_i->force_restat = 1;349 }350 351 vboxsf_close(sbi->root, params.handle);352 353 /* Update the inode with what the host has actually given us. */354 if (sf_i->force_restat)355 vboxsf_inode_revalidate(dentry);356 357 return 0;358}359 360/*361 * [dentry] contains string encoded in coding system that corresponds362 * to [sbi]->nls, we must convert it to UTF8 here.363 * Returns a shfl_string allocated through __getname (must be freed using364 * __putname), or an ERR_PTR on error.365 */366struct shfl_string *vboxsf_path_from_dentry(struct vboxsf_sbi *sbi,367 struct dentry *dentry)368{369 struct shfl_string *shfl_path;370 int path_len, out_len, nb;371 char *buf, *path;372 wchar_t uni;373 u8 *out;374 375 buf = __getname();376 if (!buf)377 return ERR_PTR(-ENOMEM);378 379 path = dentry_path_raw(dentry, buf, PATH_MAX);380 if (IS_ERR(path)) {381 __putname(buf);382 return ERR_CAST(path);383 }384 path_len = strlen(path);385 386 if (sbi->nls) {387 shfl_path = __getname();388 if (!shfl_path) {389 __putname(buf);390 return ERR_PTR(-ENOMEM);391 }392 393 out = shfl_path->string.utf8;394 out_len = PATH_MAX - SHFLSTRING_HEADER_SIZE - 1;395 396 while (path_len) {397 nb = sbi->nls->char2uni(path, path_len, &uni);398 if (nb < 0) {399 __putname(shfl_path);400 __putname(buf);401 return ERR_PTR(-EINVAL);402 }403 path += nb;404 path_len -= nb;405 406 nb = utf32_to_utf8(uni, out, out_len);407 if (nb < 0) {408 __putname(shfl_path);409 __putname(buf);410 return ERR_PTR(-ENAMETOOLONG);411 }412 out += nb;413 out_len -= nb;414 }415 *out = 0;416 shfl_path->length = out - shfl_path->string.utf8;417 shfl_path->size = shfl_path->length + 1;418 __putname(buf);419 } else {420 if ((SHFLSTRING_HEADER_SIZE + path_len + 1) > PATH_MAX) {421 __putname(buf);422 return ERR_PTR(-ENAMETOOLONG);423 }424 /*425 * dentry_path stores the name at the end of buf, but the426 * shfl_string string we return must be properly aligned.427 */428 shfl_path = (struct shfl_string *)buf;429 memmove(shfl_path->string.utf8, path, path_len);430 shfl_path->string.utf8[path_len] = 0;431 shfl_path->length = path_len;432 shfl_path->size = path_len + 1;433 }434 435 return shfl_path;436}437 438int vboxsf_nlscpy(struct vboxsf_sbi *sbi, char *name, size_t name_bound_len,439 const unsigned char *utf8_name, size_t utf8_len)440{441 const char *in;442 char *out;443 size_t out_bound_len;444 size_t in_bound_len;445 446 in = utf8_name;447 in_bound_len = utf8_len;448 449 out = name;450 /* Reserve space for terminating 0 */451 out_bound_len = name_bound_len - 1;452 453 while (in_bound_len) {454 int nb;455 unicode_t uni;456 457 nb = utf8_to_utf32(in, in_bound_len, &uni);458 if (nb < 0)459 return -EINVAL;460 461 in += nb;462 in_bound_len -= nb;463 464 nb = sbi->nls->uni2char(uni, out, out_bound_len);465 if (nb < 0)466 return nb;467 468 out += nb;469 out_bound_len -= nb;470 }471 472 *out = 0;473 474 return 0;475}476 477static struct vboxsf_dir_buf *vboxsf_dir_buf_alloc(struct list_head *list)478{479 struct vboxsf_dir_buf *b;480 481 b = kmalloc(sizeof(*b), GFP_KERNEL);482 if (!b)483 return NULL;484 485 b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL);486 if (!b->buf) {487 kfree(b);488 return NULL;489 }490 491 b->entries = 0;492 b->used = 0;493 b->free = DIR_BUFFER_SIZE;494 list_add(&b->head, list);495 496 return b;497}498 499static void vboxsf_dir_buf_free(struct vboxsf_dir_buf *b)500{501 list_del(&b->head);502 kfree(b->buf);503 kfree(b);504}505 506struct vboxsf_dir_info *vboxsf_dir_info_alloc(void)507{508 struct vboxsf_dir_info *p;509 510 p = kmalloc(sizeof(*p), GFP_KERNEL);511 if (!p)512 return NULL;513 514 INIT_LIST_HEAD(&p->info_list);515 return p;516}517 518void vboxsf_dir_info_free(struct vboxsf_dir_info *p)519{520 struct list_head *list, *pos, *tmp;521 522 list = &p->info_list;523 list_for_each_safe(pos, tmp, list) {524 struct vboxsf_dir_buf *b;525 526 b = list_entry(pos, struct vboxsf_dir_buf, head);527 vboxsf_dir_buf_free(b);528 }529 kfree(p);530}531 532int vboxsf_dir_read_all(struct vboxsf_sbi *sbi, struct vboxsf_dir_info *sf_d,533 u64 handle)534{535 struct vboxsf_dir_buf *b;536 u32 entries, size;537 int err = 0;538 void *buf;539 540 /* vboxsf_dirinfo returns 1 on end of dir */541 while (err == 0) {542 b = vboxsf_dir_buf_alloc(&sf_d->info_list);543 if (!b) {544 err = -ENOMEM;545 break;546 }547 548 buf = b->buf;549 size = b->free;550 551 err = vboxsf_dirinfo(sbi->root, handle, NULL, 0, 0,552 &size, buf, &entries);553 if (err < 0)554 break;555 556 b->entries += entries;557 b->free -= size;558 b->used += size;559 }560 561 if (b && b->used == 0)562 vboxsf_dir_buf_free(b);563 564 /* -EILSEQ means the host could not translate a filename, ignore */565 if (err > 0 || err == -EILSEQ)566 err = 0;567 568 return err;569}570