cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

utils.c (12987B)


      1// SPDX-License-Identifier: MIT
      2/*
      3 * VirtualBox Guest Shared Folders support: Utility functions.
      4 * Mainly conversion from/to VirtualBox/Linux data structures.
      5 *
      6 * Copyright (C) 2006-2018 Oracle Corporation
      7 */
      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_set
     73
     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 entries
     88			 * 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->i_atime = ns_to_timespec64(
    130				 info->access_time.ns_relative_to_unix_epoch);
    131	inode->i_ctime = ns_to_timespec64(
    132				 info->change_time.ns_relative_to_unix_epoch);
    133	inode->i_mtime = ns_to_timespec64(
    134			   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, &params);
    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 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->i_mtime;
    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 the
    225	 * page-cache for it.  Note this also gets triggered by our own writes,
    226	 * this is unavoidable.
    227	 */
    228	if (timespec64_compare(&inode->i_mtime, &prev_mtime) > 0)
    229		invalidate_inode_pages2(inode->i_mapping);
    230
    231	return 0;
    232}
    233
    234int vboxsf_getattr(struct user_namespace *mnt_userns, const struct path *path,
    235		   struct kstat *kstat, u32 request_mask, unsigned int flags)
    236{
    237	int err;
    238	struct dentry *dentry = path->dentry;
    239	struct inode *inode = d_inode(dentry);
    240	struct vboxsf_inode *sf_i = VBOXSF_I(inode);
    241
    242	switch (flags & AT_STATX_SYNC_TYPE) {
    243	case AT_STATX_DONT_SYNC:
    244		err = 0;
    245		break;
    246	case AT_STATX_FORCE_SYNC:
    247		sf_i->force_restat = 1;
    248		fallthrough;
    249	default:
    250		err = vboxsf_inode_revalidate(dentry);
    251	}
    252	if (err)
    253		return err;
    254
    255	generic_fillattr(&init_user_ns, d_inode(dentry), kstat);
    256	return 0;
    257}
    258
    259int vboxsf_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
    260		   struct iattr *iattr)
    261{
    262	struct vboxsf_inode *sf_i = VBOXSF_I(d_inode(dentry));
    263	struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);
    264	struct shfl_createparms params = {};
    265	struct shfl_fsobjinfo info = {};
    266	u32 buf_len;
    267	int err;
    268
    269	params.handle = SHFL_HANDLE_NIL;
    270	params.create_flags = SHFL_CF_ACT_OPEN_IF_EXISTS |
    271			      SHFL_CF_ACT_FAIL_IF_NEW |
    272			      SHFL_CF_ACCESS_ATTR_WRITE;
    273
    274	/* this is at least required for Posix hosts */
    275	if (iattr->ia_valid & ATTR_SIZE)
    276		params.create_flags |= SHFL_CF_ACCESS_WRITE;
    277
    278	err = vboxsf_create_at_dentry(dentry, &params);
    279	if (err || params.result != SHFL_FILE_EXISTS)
    280		return err ? err : -ENOENT;
    281
    282#define mode_set(r) ((iattr->ia_mode & (S_##r)) ? SHFL_UNIX_##r : 0)
    283
    284	/*
    285	 * Setting the file size and setting the other attributes has to
    286	 * be handled separately.
    287	 */
    288	if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) {
    289		if (iattr->ia_valid & ATTR_MODE) {
    290			info.attr.mode = mode_set(IRUSR);
    291			info.attr.mode |= mode_set(IWUSR);
    292			info.attr.mode |= mode_set(IXUSR);
    293			info.attr.mode |= mode_set(IRGRP);
    294			info.attr.mode |= mode_set(IWGRP);
    295			info.attr.mode |= mode_set(IXGRP);
    296			info.attr.mode |= mode_set(IROTH);
    297			info.attr.mode |= mode_set(IWOTH);
    298			info.attr.mode |= mode_set(IXOTH);
    299
    300			if (iattr->ia_mode & S_IFDIR)
    301				info.attr.mode |= SHFL_TYPE_DIRECTORY;
    302			else
    303				info.attr.mode |= SHFL_TYPE_FILE;
    304		}
    305
    306		if (iattr->ia_valid & ATTR_ATIME)
    307			info.access_time.ns_relative_to_unix_epoch =
    308					    timespec64_to_ns(&iattr->ia_atime);
    309
    310		if (iattr->ia_valid & ATTR_MTIME)
    311			info.modification_time.ns_relative_to_unix_epoch =
    312					    timespec64_to_ns(&iattr->ia_mtime);
    313
    314		/*
    315		 * Ignore ctime (inode change time) as it can't be set
    316		 * from userland anyway.
    317		 */
    318
    319		buf_len = sizeof(info);
    320		err = vboxsf_fsinfo(sbi->root, params.handle,
    321				    SHFL_INFO_SET | SHFL_INFO_FILE, &buf_len,
    322				    &info);
    323		if (err) {
    324			vboxsf_close(sbi->root, params.handle);
    325			return err;
    326		}
    327
    328		/* the host may have given us different attr then requested */
    329		sf_i->force_restat = 1;
    330	}
    331
    332#undef mode_set
    333
    334	if (iattr->ia_valid & ATTR_SIZE) {
    335		memset(&info, 0, sizeof(info));
    336		info.size = iattr->ia_size;
    337		buf_len = sizeof(info);
    338		err = vboxsf_fsinfo(sbi->root, params.handle,
    339				    SHFL_INFO_SET | SHFL_INFO_SIZE, &buf_len,
    340				    &info);
    341		if (err) {
    342			vboxsf_close(sbi->root, params.handle);
    343			return err;
    344		}
    345
    346		/* the host may have given us different attr then requested */
    347		sf_i->force_restat = 1;
    348	}
    349
    350	vboxsf_close(sbi->root, params.handle);
    351
    352	/* Update the inode with what the host has actually given us. */
    353	if (sf_i->force_restat)
    354		vboxsf_inode_revalidate(dentry);
    355
    356	return 0;
    357}
    358
    359/*
    360 * [dentry] contains string encoded in coding system that corresponds
    361 * to [sbi]->nls, we must convert it to UTF8 here.
    362 * Returns a shfl_string allocated through __getname (must be freed using
    363 * __putname), or an ERR_PTR on error.
    364 */
    365struct shfl_string *vboxsf_path_from_dentry(struct vboxsf_sbi *sbi,
    366					    struct dentry *dentry)
    367{
    368	struct shfl_string *shfl_path;
    369	int path_len, out_len, nb;
    370	char *buf, *path;
    371	wchar_t uni;
    372	u8 *out;
    373
    374	buf = __getname();
    375	if (!buf)
    376		return ERR_PTR(-ENOMEM);
    377
    378	path = dentry_path_raw(dentry, buf, PATH_MAX);
    379	if (IS_ERR(path)) {
    380		__putname(buf);
    381		return ERR_CAST(path);
    382	}
    383	path_len = strlen(path);
    384
    385	if (sbi->nls) {
    386		shfl_path = __getname();
    387		if (!shfl_path) {
    388			__putname(buf);
    389			return ERR_PTR(-ENOMEM);
    390		}
    391
    392		out = shfl_path->string.utf8;
    393		out_len = PATH_MAX - SHFLSTRING_HEADER_SIZE - 1;
    394
    395		while (path_len) {
    396			nb = sbi->nls->char2uni(path, path_len, &uni);
    397			if (nb < 0) {
    398				__putname(shfl_path);
    399				__putname(buf);
    400				return ERR_PTR(-EINVAL);
    401			}
    402			path += nb;
    403			path_len -= nb;
    404
    405			nb = utf32_to_utf8(uni, out, out_len);
    406			if (nb < 0) {
    407				__putname(shfl_path);
    408				__putname(buf);
    409				return ERR_PTR(-ENAMETOOLONG);
    410			}
    411			out += nb;
    412			out_len -= nb;
    413		}
    414		*out = 0;
    415		shfl_path->length = out - shfl_path->string.utf8;
    416		shfl_path->size = shfl_path->length + 1;
    417		__putname(buf);
    418	} else {
    419		if ((SHFLSTRING_HEADER_SIZE + path_len + 1) > PATH_MAX) {
    420			__putname(buf);
    421			return ERR_PTR(-ENAMETOOLONG);
    422		}
    423		/*
    424		 * dentry_path stores the name at the end of buf, but the
    425		 * shfl_string string we return must be properly aligned.
    426		 */
    427		shfl_path = (struct shfl_string *)buf;
    428		memmove(shfl_path->string.utf8, path, path_len);
    429		shfl_path->string.utf8[path_len] = 0;
    430		shfl_path->length = path_len;
    431		shfl_path->size = path_len + 1;
    432	}
    433
    434	return shfl_path;
    435}
    436
    437int vboxsf_nlscpy(struct vboxsf_sbi *sbi, char *name, size_t name_bound_len,
    438		  const unsigned char *utf8_name, size_t utf8_len)
    439{
    440	const char *in;
    441	char *out;
    442	size_t out_len;
    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	out_len = 0;
    451	/* Reserve space for terminating 0 */
    452	out_bound_len = name_bound_len - 1;
    453
    454	while (in_bound_len) {
    455		int nb;
    456		unicode_t uni;
    457
    458		nb = utf8_to_utf32(in, in_bound_len, &uni);
    459		if (nb < 0)
    460			return -EINVAL;
    461
    462		in += nb;
    463		in_bound_len -= nb;
    464
    465		nb = sbi->nls->uni2char(uni, out, out_bound_len);
    466		if (nb < 0)
    467			return nb;
    468
    469		out += nb;
    470		out_bound_len -= nb;
    471		out_len += nb;
    472	}
    473
    474	*out = 0;
    475
    476	return 0;
    477}
    478
    479static struct vboxsf_dir_buf *vboxsf_dir_buf_alloc(struct list_head *list)
    480{
    481	struct vboxsf_dir_buf *b;
    482
    483	b = kmalloc(sizeof(*b), GFP_KERNEL);
    484	if (!b)
    485		return NULL;
    486
    487	b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL);
    488	if (!b->buf) {
    489		kfree(b);
    490		return NULL;
    491	}
    492
    493	b->entries = 0;
    494	b->used = 0;
    495	b->free = DIR_BUFFER_SIZE;
    496	list_add(&b->head, list);
    497
    498	return b;
    499}
    500
    501static void vboxsf_dir_buf_free(struct vboxsf_dir_buf *b)
    502{
    503	list_del(&b->head);
    504	kfree(b->buf);
    505	kfree(b);
    506}
    507
    508struct vboxsf_dir_info *vboxsf_dir_info_alloc(void)
    509{
    510	struct vboxsf_dir_info *p;
    511
    512	p = kmalloc(sizeof(*p), GFP_KERNEL);
    513	if (!p)
    514		return NULL;
    515
    516	INIT_LIST_HEAD(&p->info_list);
    517	return p;
    518}
    519
    520void vboxsf_dir_info_free(struct vboxsf_dir_info *p)
    521{
    522	struct list_head *list, *pos, *tmp;
    523
    524	list = &p->info_list;
    525	list_for_each_safe(pos, tmp, list) {
    526		struct vboxsf_dir_buf *b;
    527
    528		b = list_entry(pos, struct vboxsf_dir_buf, head);
    529		vboxsf_dir_buf_free(b);
    530	}
    531	kfree(p);
    532}
    533
    534int vboxsf_dir_read_all(struct vboxsf_sbi *sbi, struct vboxsf_dir_info *sf_d,
    535			u64 handle)
    536{
    537	struct vboxsf_dir_buf *b;
    538	u32 entries, size;
    539	int err = 0;
    540	void *buf;
    541
    542	/* vboxsf_dirinfo returns 1 on end of dir */
    543	while (err == 0) {
    544		b = vboxsf_dir_buf_alloc(&sf_d->info_list);
    545		if (!b) {
    546			err = -ENOMEM;
    547			break;
    548		}
    549
    550		buf = b->buf;
    551		size = b->free;
    552
    553		err = vboxsf_dirinfo(sbi->root, handle, NULL, 0, 0,
    554				     &size, buf, &entries);
    555		if (err < 0)
    556			break;
    557
    558		b->entries += entries;
    559		b->free -= size;
    560		b->used += size;
    561	}
    562
    563	if (b && b->used == 0)
    564		vboxsf_dir_buf_free(b);
    565
    566	/* -EILSEQ means the host could not translate a filename, ignore */
    567	if (err > 0 || err == -EILSEQ)
    568		err = 0;
    569
    570	return err;
    571}