162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VirtualBox Guest Shared Folders support: Utility functions. 462306a36Sopenharmony_ci * Mainly conversion from/to VirtualBox/Linux data structures. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2006-2018 Oracle Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/namei.h> 1062306a36Sopenharmony_ci#include <linux/nls.h> 1162306a36Sopenharmony_ci#include <linux/sizes.h> 1262306a36Sopenharmony_ci#include <linux/pagemap.h> 1362306a36Sopenharmony_ci#include <linux/vfs.h> 1462306a36Sopenharmony_ci#include "vfsmod.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct inode *vboxsf_new_inode(struct super_block *sb) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct vboxsf_sbi *sbi = VBOXSF_SBI(sb); 1962306a36Sopenharmony_ci struct inode *inode; 2062306a36Sopenharmony_ci unsigned long flags; 2162306a36Sopenharmony_ci int cursor, ret; 2262306a36Sopenharmony_ci u32 gen; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci inode = new_inode(sb); 2562306a36Sopenharmony_ci if (!inode) 2662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci idr_preload(GFP_KERNEL); 2962306a36Sopenharmony_ci spin_lock_irqsave(&sbi->ino_idr_lock, flags); 3062306a36Sopenharmony_ci cursor = idr_get_cursor(&sbi->ino_idr); 3162306a36Sopenharmony_ci ret = idr_alloc_cyclic(&sbi->ino_idr, inode, 1, 0, GFP_ATOMIC); 3262306a36Sopenharmony_ci if (ret >= 0 && ret < cursor) 3362306a36Sopenharmony_ci sbi->next_generation++; 3462306a36Sopenharmony_ci gen = sbi->next_generation; 3562306a36Sopenharmony_ci spin_unlock_irqrestore(&sbi->ino_idr_lock, flags); 3662306a36Sopenharmony_ci idr_preload_end(); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (ret < 0) { 3962306a36Sopenharmony_ci iput(inode); 4062306a36Sopenharmony_ci return ERR_PTR(ret); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci inode->i_ino = ret; 4462306a36Sopenharmony_ci inode->i_generation = gen; 4562306a36Sopenharmony_ci return inode; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* set [inode] attributes based on [info], uid/gid based on [sbi] */ 4962306a36Sopenharmony_ciint vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode, 5062306a36Sopenharmony_ci const struct shfl_fsobjinfo *info, bool reinit) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci const struct shfl_fsobjattr *attr; 5362306a36Sopenharmony_ci s64 allocated; 5462306a36Sopenharmony_ci umode_t mode; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci attr = &info->attr; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define mode_set(r) ((attr->mode & (SHFL_UNIX_##r)) ? (S_##r) : 0) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci mode = mode_set(IRUSR); 6162306a36Sopenharmony_ci mode |= mode_set(IWUSR); 6262306a36Sopenharmony_ci mode |= mode_set(IXUSR); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci mode |= mode_set(IRGRP); 6562306a36Sopenharmony_ci mode |= mode_set(IWGRP); 6662306a36Sopenharmony_ci mode |= mode_set(IXGRP); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci mode |= mode_set(IROTH); 6962306a36Sopenharmony_ci mode |= mode_set(IWOTH); 7062306a36Sopenharmony_ci mode |= mode_set(IXOTH); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#undef mode_set 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* We use the host-side values for these */ 7562306a36Sopenharmony_ci inode->i_flags |= S_NOATIME | S_NOCMTIME; 7662306a36Sopenharmony_ci inode->i_mapping->a_ops = &vboxsf_reg_aops; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (SHFL_IS_DIRECTORY(attr->mode)) { 7962306a36Sopenharmony_ci if (sbi->o.dmode_set) 8062306a36Sopenharmony_ci mode = sbi->o.dmode; 8162306a36Sopenharmony_ci mode &= ~sbi->o.dmask; 8262306a36Sopenharmony_ci mode |= S_IFDIR; 8362306a36Sopenharmony_ci if (!reinit) { 8462306a36Sopenharmony_ci inode->i_op = &vboxsf_dir_iops; 8562306a36Sopenharmony_ci inode->i_fop = &vboxsf_dir_fops; 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * XXX: this probably should be set to the number of entries 8862306a36Sopenharmony_ci * in the directory plus two (. ..) 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci set_nlink(inode, 1); 9162306a36Sopenharmony_ci } else if (!S_ISDIR(inode->i_mode)) 9262306a36Sopenharmony_ci return -ESTALE; 9362306a36Sopenharmony_ci inode->i_mode = mode; 9462306a36Sopenharmony_ci } else if (SHFL_IS_SYMLINK(attr->mode)) { 9562306a36Sopenharmony_ci if (sbi->o.fmode_set) 9662306a36Sopenharmony_ci mode = sbi->o.fmode; 9762306a36Sopenharmony_ci mode &= ~sbi->o.fmask; 9862306a36Sopenharmony_ci mode |= S_IFLNK; 9962306a36Sopenharmony_ci if (!reinit) { 10062306a36Sopenharmony_ci inode->i_op = &vboxsf_lnk_iops; 10162306a36Sopenharmony_ci set_nlink(inode, 1); 10262306a36Sopenharmony_ci } else if (!S_ISLNK(inode->i_mode)) 10362306a36Sopenharmony_ci return -ESTALE; 10462306a36Sopenharmony_ci inode->i_mode = mode; 10562306a36Sopenharmony_ci } else { 10662306a36Sopenharmony_ci if (sbi->o.fmode_set) 10762306a36Sopenharmony_ci mode = sbi->o.fmode; 10862306a36Sopenharmony_ci mode &= ~sbi->o.fmask; 10962306a36Sopenharmony_ci mode |= S_IFREG; 11062306a36Sopenharmony_ci if (!reinit) { 11162306a36Sopenharmony_ci inode->i_op = &vboxsf_reg_iops; 11262306a36Sopenharmony_ci inode->i_fop = &vboxsf_reg_fops; 11362306a36Sopenharmony_ci set_nlink(inode, 1); 11462306a36Sopenharmony_ci } else if (!S_ISREG(inode->i_mode)) 11562306a36Sopenharmony_ci return -ESTALE; 11662306a36Sopenharmony_ci inode->i_mode = mode; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci inode->i_uid = sbi->o.uid; 12062306a36Sopenharmony_ci inode->i_gid = sbi->o.gid; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci inode->i_size = info->size; 12362306a36Sopenharmony_ci inode->i_blkbits = 12; 12462306a36Sopenharmony_ci /* i_blocks always in units of 512 bytes! */ 12562306a36Sopenharmony_ci allocated = info->allocated + 511; 12662306a36Sopenharmony_ci do_div(allocated, 512); 12762306a36Sopenharmony_ci inode->i_blocks = allocated; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci inode->i_atime = ns_to_timespec64( 13062306a36Sopenharmony_ci info->access_time.ns_relative_to_unix_epoch); 13162306a36Sopenharmony_ci inode_set_ctime_to_ts(inode, 13262306a36Sopenharmony_ci ns_to_timespec64(info->change_time.ns_relative_to_unix_epoch)); 13362306a36Sopenharmony_ci inode->i_mtime = ns_to_timespec64( 13462306a36Sopenharmony_ci info->modification_time.ns_relative_to_unix_epoch); 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint vboxsf_create_at_dentry(struct dentry *dentry, 13962306a36Sopenharmony_ci struct shfl_createparms *params) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb); 14262306a36Sopenharmony_ci struct shfl_string *path; 14362306a36Sopenharmony_ci int err; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci path = vboxsf_path_from_dentry(sbi, dentry); 14662306a36Sopenharmony_ci if (IS_ERR(path)) 14762306a36Sopenharmony_ci return PTR_ERR(path); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci err = vboxsf_create(sbi->root, path, params); 15062306a36Sopenharmony_ci __putname(path); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return err; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint vboxsf_stat(struct vboxsf_sbi *sbi, struct shfl_string *path, 15662306a36Sopenharmony_ci struct shfl_fsobjinfo *info) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct shfl_createparms params = {}; 15962306a36Sopenharmony_ci int err; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci params.handle = SHFL_HANDLE_NIL; 16262306a36Sopenharmony_ci params.create_flags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci err = vboxsf_create(sbi->root, path, ¶ms); 16562306a36Sopenharmony_ci if (err) 16662306a36Sopenharmony_ci return err; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (params.result != SHFL_FILE_EXISTS) 16962306a36Sopenharmony_ci return -ENOENT; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (info) 17262306a36Sopenharmony_ci *info = params.info; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint vboxsf_stat_dentry(struct dentry *dentry, struct shfl_fsobjinfo *info) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb); 18062306a36Sopenharmony_ci struct shfl_string *path; 18162306a36Sopenharmony_ci int err; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci path = vboxsf_path_from_dentry(sbi, dentry); 18462306a36Sopenharmony_ci if (IS_ERR(path)) 18562306a36Sopenharmony_ci return PTR_ERR(path); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci err = vboxsf_stat(sbi, path, info); 18862306a36Sopenharmony_ci __putname(path); 18962306a36Sopenharmony_ci return err; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ciint vboxsf_inode_revalidate(struct dentry *dentry) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct vboxsf_sbi *sbi; 19562306a36Sopenharmony_ci struct vboxsf_inode *sf_i; 19662306a36Sopenharmony_ci struct shfl_fsobjinfo info; 19762306a36Sopenharmony_ci struct timespec64 prev_mtime; 19862306a36Sopenharmony_ci struct inode *inode; 19962306a36Sopenharmony_ci int err; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!dentry || !d_really_is_positive(dentry)) 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci inode = d_inode(dentry); 20562306a36Sopenharmony_ci prev_mtime = inode->i_mtime; 20662306a36Sopenharmony_ci sf_i = VBOXSF_I(inode); 20762306a36Sopenharmony_ci sbi = VBOXSF_SBI(dentry->d_sb); 20862306a36Sopenharmony_ci if (!sf_i->force_restat) { 20962306a36Sopenharmony_ci if (time_before(jiffies, dentry->d_time + sbi->o.ttl)) 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci err = vboxsf_stat_dentry(dentry, &info); 21462306a36Sopenharmony_ci if (err) 21562306a36Sopenharmony_ci return err; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci dentry->d_time = jiffies; 21862306a36Sopenharmony_ci sf_i->force_restat = 0; 21962306a36Sopenharmony_ci err = vboxsf_init_inode(sbi, inode, &info, true); 22062306a36Sopenharmony_ci if (err) 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * If the file was changed on the host side we need to invalidate the 22562306a36Sopenharmony_ci * page-cache for it. Note this also gets triggered by our own writes, 22662306a36Sopenharmony_ci * this is unavoidable. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci if (timespec64_compare(&inode->i_mtime, &prev_mtime) > 0) 22962306a36Sopenharmony_ci invalidate_inode_pages2(inode->i_mapping); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciint vboxsf_getattr(struct mnt_idmap *idmap, const struct path *path, 23562306a36Sopenharmony_ci struct kstat *kstat, u32 request_mask, unsigned int flags) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci int err; 23862306a36Sopenharmony_ci struct dentry *dentry = path->dentry; 23962306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 24062306a36Sopenharmony_ci struct vboxsf_inode *sf_i = VBOXSF_I(inode); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci switch (flags & AT_STATX_SYNC_TYPE) { 24362306a36Sopenharmony_ci case AT_STATX_DONT_SYNC: 24462306a36Sopenharmony_ci err = 0; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case AT_STATX_FORCE_SYNC: 24762306a36Sopenharmony_ci sf_i->force_restat = 1; 24862306a36Sopenharmony_ci fallthrough; 24962306a36Sopenharmony_ci default: 25062306a36Sopenharmony_ci err = vboxsf_inode_revalidate(dentry); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci if (err) 25362306a36Sopenharmony_ci return err; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(dentry), kstat); 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciint vboxsf_setattr(struct mnt_idmap *idmap, struct dentry *dentry, 26062306a36Sopenharmony_ci struct iattr *iattr) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct vboxsf_inode *sf_i = VBOXSF_I(d_inode(dentry)); 26362306a36Sopenharmony_ci struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb); 26462306a36Sopenharmony_ci struct shfl_createparms params = {}; 26562306a36Sopenharmony_ci struct shfl_fsobjinfo info = {}; 26662306a36Sopenharmony_ci u32 buf_len; 26762306a36Sopenharmony_ci int err; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci params.handle = SHFL_HANDLE_NIL; 27062306a36Sopenharmony_ci params.create_flags = SHFL_CF_ACT_OPEN_IF_EXISTS | 27162306a36Sopenharmony_ci SHFL_CF_ACT_FAIL_IF_NEW | 27262306a36Sopenharmony_ci SHFL_CF_ACCESS_ATTR_WRITE; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* this is at least required for Posix hosts */ 27562306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_SIZE) 27662306a36Sopenharmony_ci params.create_flags |= SHFL_CF_ACCESS_WRITE; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci err = vboxsf_create_at_dentry(dentry, ¶ms); 27962306a36Sopenharmony_ci if (err || params.result != SHFL_FILE_EXISTS) 28062306a36Sopenharmony_ci return err ? err : -ENOENT; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci#define mode_set(r) ((iattr->ia_mode & (S_##r)) ? SHFL_UNIX_##r : 0) 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * Setting the file size and setting the other attributes has to 28662306a36Sopenharmony_ci * be handled separately. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) { 28962306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_MODE) { 29062306a36Sopenharmony_ci info.attr.mode = mode_set(IRUSR); 29162306a36Sopenharmony_ci info.attr.mode |= mode_set(IWUSR); 29262306a36Sopenharmony_ci info.attr.mode |= mode_set(IXUSR); 29362306a36Sopenharmony_ci info.attr.mode |= mode_set(IRGRP); 29462306a36Sopenharmony_ci info.attr.mode |= mode_set(IWGRP); 29562306a36Sopenharmony_ci info.attr.mode |= mode_set(IXGRP); 29662306a36Sopenharmony_ci info.attr.mode |= mode_set(IROTH); 29762306a36Sopenharmony_ci info.attr.mode |= mode_set(IWOTH); 29862306a36Sopenharmony_ci info.attr.mode |= mode_set(IXOTH); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (iattr->ia_mode & S_IFDIR) 30162306a36Sopenharmony_ci info.attr.mode |= SHFL_TYPE_DIRECTORY; 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci info.attr.mode |= SHFL_TYPE_FILE; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_ATIME) 30762306a36Sopenharmony_ci info.access_time.ns_relative_to_unix_epoch = 30862306a36Sopenharmony_ci timespec64_to_ns(&iattr->ia_atime); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_MTIME) 31162306a36Sopenharmony_ci info.modification_time.ns_relative_to_unix_epoch = 31262306a36Sopenharmony_ci timespec64_to_ns(&iattr->ia_mtime); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * Ignore ctime (inode change time) as it can't be set 31662306a36Sopenharmony_ci * from userland anyway. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci buf_len = sizeof(info); 32062306a36Sopenharmony_ci err = vboxsf_fsinfo(sbi->root, params.handle, 32162306a36Sopenharmony_ci SHFL_INFO_SET | SHFL_INFO_FILE, &buf_len, 32262306a36Sopenharmony_ci &info); 32362306a36Sopenharmony_ci if (err) { 32462306a36Sopenharmony_ci vboxsf_close(sbi->root, params.handle); 32562306a36Sopenharmony_ci return err; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* the host may have given us different attr then requested */ 32962306a36Sopenharmony_ci sf_i->force_restat = 1; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci#undef mode_set 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_SIZE) { 33562306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 33662306a36Sopenharmony_ci info.size = iattr->ia_size; 33762306a36Sopenharmony_ci buf_len = sizeof(info); 33862306a36Sopenharmony_ci err = vboxsf_fsinfo(sbi->root, params.handle, 33962306a36Sopenharmony_ci SHFL_INFO_SET | SHFL_INFO_SIZE, &buf_len, 34062306a36Sopenharmony_ci &info); 34162306a36Sopenharmony_ci if (err) { 34262306a36Sopenharmony_ci vboxsf_close(sbi->root, params.handle); 34362306a36Sopenharmony_ci return err; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* the host may have given us different attr then requested */ 34762306a36Sopenharmony_ci sf_i->force_restat = 1; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci vboxsf_close(sbi->root, params.handle); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* Update the inode with what the host has actually given us. */ 35362306a36Sopenharmony_ci if (sf_i->force_restat) 35462306a36Sopenharmony_ci vboxsf_inode_revalidate(dentry); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * [dentry] contains string encoded in coding system that corresponds 36162306a36Sopenharmony_ci * to [sbi]->nls, we must convert it to UTF8 here. 36262306a36Sopenharmony_ci * Returns a shfl_string allocated through __getname (must be freed using 36362306a36Sopenharmony_ci * __putname), or an ERR_PTR on error. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_cistruct shfl_string *vboxsf_path_from_dentry(struct vboxsf_sbi *sbi, 36662306a36Sopenharmony_ci struct dentry *dentry) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct shfl_string *shfl_path; 36962306a36Sopenharmony_ci int path_len, out_len, nb; 37062306a36Sopenharmony_ci char *buf, *path; 37162306a36Sopenharmony_ci wchar_t uni; 37262306a36Sopenharmony_ci u8 *out; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci buf = __getname(); 37562306a36Sopenharmony_ci if (!buf) 37662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci path = dentry_path_raw(dentry, buf, PATH_MAX); 37962306a36Sopenharmony_ci if (IS_ERR(path)) { 38062306a36Sopenharmony_ci __putname(buf); 38162306a36Sopenharmony_ci return ERR_CAST(path); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci path_len = strlen(path); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (sbi->nls) { 38662306a36Sopenharmony_ci shfl_path = __getname(); 38762306a36Sopenharmony_ci if (!shfl_path) { 38862306a36Sopenharmony_ci __putname(buf); 38962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci out = shfl_path->string.utf8; 39362306a36Sopenharmony_ci out_len = PATH_MAX - SHFLSTRING_HEADER_SIZE - 1; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci while (path_len) { 39662306a36Sopenharmony_ci nb = sbi->nls->char2uni(path, path_len, &uni); 39762306a36Sopenharmony_ci if (nb < 0) { 39862306a36Sopenharmony_ci __putname(shfl_path); 39962306a36Sopenharmony_ci __putname(buf); 40062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci path += nb; 40362306a36Sopenharmony_ci path_len -= nb; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci nb = utf32_to_utf8(uni, out, out_len); 40662306a36Sopenharmony_ci if (nb < 0) { 40762306a36Sopenharmony_ci __putname(shfl_path); 40862306a36Sopenharmony_ci __putname(buf); 40962306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci out += nb; 41262306a36Sopenharmony_ci out_len -= nb; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci *out = 0; 41562306a36Sopenharmony_ci shfl_path->length = out - shfl_path->string.utf8; 41662306a36Sopenharmony_ci shfl_path->size = shfl_path->length + 1; 41762306a36Sopenharmony_ci __putname(buf); 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci if ((SHFLSTRING_HEADER_SIZE + path_len + 1) > PATH_MAX) { 42062306a36Sopenharmony_ci __putname(buf); 42162306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * dentry_path stores the name at the end of buf, but the 42562306a36Sopenharmony_ci * shfl_string string we return must be properly aligned. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci shfl_path = (struct shfl_string *)buf; 42862306a36Sopenharmony_ci memmove(shfl_path->string.utf8, path, path_len); 42962306a36Sopenharmony_ci shfl_path->string.utf8[path_len] = 0; 43062306a36Sopenharmony_ci shfl_path->length = path_len; 43162306a36Sopenharmony_ci shfl_path->size = path_len + 1; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return shfl_path; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ciint vboxsf_nlscpy(struct vboxsf_sbi *sbi, char *name, size_t name_bound_len, 43862306a36Sopenharmony_ci const unsigned char *utf8_name, size_t utf8_len) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci const char *in; 44162306a36Sopenharmony_ci char *out; 44262306a36Sopenharmony_ci size_t out_len; 44362306a36Sopenharmony_ci size_t out_bound_len; 44462306a36Sopenharmony_ci size_t in_bound_len; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci in = utf8_name; 44762306a36Sopenharmony_ci in_bound_len = utf8_len; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci out = name; 45062306a36Sopenharmony_ci out_len = 0; 45162306a36Sopenharmony_ci /* Reserve space for terminating 0 */ 45262306a36Sopenharmony_ci out_bound_len = name_bound_len - 1; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci while (in_bound_len) { 45562306a36Sopenharmony_ci int nb; 45662306a36Sopenharmony_ci unicode_t uni; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci nb = utf8_to_utf32(in, in_bound_len, &uni); 45962306a36Sopenharmony_ci if (nb < 0) 46062306a36Sopenharmony_ci return -EINVAL; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci in += nb; 46362306a36Sopenharmony_ci in_bound_len -= nb; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci nb = sbi->nls->uni2char(uni, out, out_bound_len); 46662306a36Sopenharmony_ci if (nb < 0) 46762306a36Sopenharmony_ci return nb; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci out += nb; 47062306a36Sopenharmony_ci out_bound_len -= nb; 47162306a36Sopenharmony_ci out_len += nb; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci *out = 0; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic struct vboxsf_dir_buf *vboxsf_dir_buf_alloc(struct list_head *list) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct vboxsf_dir_buf *b; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci b = kmalloc(sizeof(*b), GFP_KERNEL); 48462306a36Sopenharmony_ci if (!b) 48562306a36Sopenharmony_ci return NULL; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL); 48862306a36Sopenharmony_ci if (!b->buf) { 48962306a36Sopenharmony_ci kfree(b); 49062306a36Sopenharmony_ci return NULL; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci b->entries = 0; 49462306a36Sopenharmony_ci b->used = 0; 49562306a36Sopenharmony_ci b->free = DIR_BUFFER_SIZE; 49662306a36Sopenharmony_ci list_add(&b->head, list); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return b; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void vboxsf_dir_buf_free(struct vboxsf_dir_buf *b) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci list_del(&b->head); 50462306a36Sopenharmony_ci kfree(b->buf); 50562306a36Sopenharmony_ci kfree(b); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistruct vboxsf_dir_info *vboxsf_dir_info_alloc(void) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct vboxsf_dir_info *p; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci p = kmalloc(sizeof(*p), GFP_KERNEL); 51362306a36Sopenharmony_ci if (!p) 51462306a36Sopenharmony_ci return NULL; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci INIT_LIST_HEAD(&p->info_list); 51762306a36Sopenharmony_ci return p; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_civoid vboxsf_dir_info_free(struct vboxsf_dir_info *p) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct list_head *list, *pos, *tmp; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci list = &p->info_list; 52562306a36Sopenharmony_ci list_for_each_safe(pos, tmp, list) { 52662306a36Sopenharmony_ci struct vboxsf_dir_buf *b; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci b = list_entry(pos, struct vboxsf_dir_buf, head); 52962306a36Sopenharmony_ci vboxsf_dir_buf_free(b); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci kfree(p); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciint vboxsf_dir_read_all(struct vboxsf_sbi *sbi, struct vboxsf_dir_info *sf_d, 53562306a36Sopenharmony_ci u64 handle) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct vboxsf_dir_buf *b; 53862306a36Sopenharmony_ci u32 entries, size; 53962306a36Sopenharmony_ci int err = 0; 54062306a36Sopenharmony_ci void *buf; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* vboxsf_dirinfo returns 1 on end of dir */ 54362306a36Sopenharmony_ci while (err == 0) { 54462306a36Sopenharmony_ci b = vboxsf_dir_buf_alloc(&sf_d->info_list); 54562306a36Sopenharmony_ci if (!b) { 54662306a36Sopenharmony_ci err = -ENOMEM; 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci buf = b->buf; 55162306a36Sopenharmony_ci size = b->free; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci err = vboxsf_dirinfo(sbi->root, handle, NULL, 0, 0, 55462306a36Sopenharmony_ci &size, buf, &entries); 55562306a36Sopenharmony_ci if (err < 0) 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci b->entries += entries; 55962306a36Sopenharmony_ci b->free -= size; 56062306a36Sopenharmony_ci b->used += size; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (b && b->used == 0) 56462306a36Sopenharmony_ci vboxsf_dir_buf_free(b); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* -EILSEQ means the host could not translate a filename, ignore */ 56762306a36Sopenharmony_ci if (err > 0 || err == -EILSEQ) 56862306a36Sopenharmony_ci err = 0; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return err; 57162306a36Sopenharmony_ci} 572