brintos

brintos / linux-shallow public Read only

0
0
Text · 12.7 KiB · 9515bbf Raw
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, &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 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, &params);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