File Index
Files and directories
Sign in
FILE

dir.c

File preview with syntax highlighting.

linux-6.19.14/fs/vboxsf/dir.c C
  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};
~/ Read-only file preview.