VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/sharedfolders/dirops.c@ 29743

Last change on this file since 29743 was 28998, checked in by vboxsync, 15 years ago

Linux Additions: rename the vboxvfs module to vboxsf to make it load by demand of the Linux kernel

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.5 KB
Line 
1/** @file
2 *
3 * vboxsf -- VirtualBox Guest Additions for Linux:
4 * Directory inode and file operations
5 */
6
7/*
8 * Copyright (C) 2006-2007 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "vfsmod.h"
20
21static int
22sf_dir_open (struct inode *inode, struct file *file)
23{
24 int rc;
25 int err;
26 struct sf_glob_info *sf_g = GET_GLOB_INFO (inode->i_sb);
27 struct sf_dir_info *sf_d;
28 struct sf_inode_info *sf_i = GET_INODE_INFO (inode);
29 SHFLCREATEPARMS params;
30
31 TRACE ();
32 BUG_ON (!sf_g);
33 BUG_ON (!sf_i);
34
35 if (file->private_data) {
36 LogFunc(("dir_open called on already opened directory %s\n",
37 sf_i->path->String.utf8));
38 return 0;
39 }
40
41 RT_ZERO(params);
42
43 sf_d = sf_dir_info_alloc ();
44
45 if (!sf_d) {
46 LogRelFunc(("could not allocate directory info for %s\n",
47 sf_i->path->String.utf8));
48 return -ENOMEM;
49 }
50
51 params.CreateFlags = 0
52 | SHFL_CF_DIRECTORY
53 | SHFL_CF_ACT_OPEN_IF_EXISTS
54 | SHFL_CF_ACT_FAIL_IF_NEW
55 | SHFL_CF_ACCESS_READ
56 ;
57
58 LogFunc(("sf_dir_open: calling vboxCallCreate, folder %s, flags %#x\n",
59 sf_i->path->String.utf8, params.CreateFlags));
60 rc = vboxCallCreate (&client_handle, &sf_g->map, sf_i->path, &params);
61 if (RT_FAILURE (rc)) {
62 LogFunc(("vboxCallCreate(%s) failed rc=%Rrc\n",
63 sf_i->path->String.utf8, rc));
64 sf_dir_info_free (sf_d);
65 return -EPERM;
66 }
67
68 if (params.Result != SHFL_FILE_EXISTS) {
69 LogFunc(("directory %s does not exist\n", sf_i->path->String.utf8));
70 sf_dir_info_free (sf_d);
71 return -ENOENT;
72 }
73
74 err = sf_dir_read_all (sf_g, sf_i, sf_d, params.Handle);
75 if (err) {
76 rc = vboxCallClose (&client_handle, &sf_g->map, params.Handle);
77 if (RT_FAILURE (rc)) {
78 LogFunc(("vboxCallClose(%s) after err=%d failed rc=%Rrc\n",
79 sf_i->path->String.utf8, err, rc));
80 }
81 sf_dir_info_free (sf_d);
82 return err;
83 }
84
85
86 rc = vboxCallClose (&client_handle, &sf_g->map, params.Handle);
87 if (RT_FAILURE (rc)) {
88 LogFunc(("vboxCallClose(%s) failed rc=%Rrc\n",
89 sf_i->path->String.utf8, rc));
90 }
91
92 file->private_data = sf_d;
93 return 0;
94}
95
96/* This is called when reference count of [file] goes to zero. Notify
97 the host that it can free whatever is associated with this
98 directory and deallocate our own internal buffers */
99static int
100sf_dir_release (struct inode *inode, struct file *file)
101{
102 struct sf_inode_info *sf_i = GET_INODE_INFO (inode);
103
104 TRACE ();
105 BUG_ON (!sf_i);
106
107 if (file->private_data) {
108 sf_dir_info_free (file->private_data);
109 }
110 return 0;
111}
112
113/* Extract element ([dir]->f_pos) from the directory [dir] into
114 [d_name], return:
115 0 all ok
116 1 end reached
117 -errno some form of error*/
118static int
119sf_getdent (struct file *dir, char d_name[NAME_MAX])
120{
121 loff_t cur;
122 struct sf_glob_info *sf_g;
123 struct sf_dir_info *sf_d;
124 struct list_head *pos, *list;
125
126 TRACE ();
127 sf_g = GET_GLOB_INFO (dir->f_dentry->d_inode->i_sb);
128 sf_d = dir->private_data;
129
130 BUG_ON (!sf_g);
131 BUG_ON (!sf_d);
132
133 cur = 0;
134 list = &sf_d->info_list;
135 list_for_each (pos, list) {
136 struct sf_dir_buf *b;
137 SHFLDIRINFO *info;
138 loff_t i;
139 size_t name_len;
140 char *name_ptr;
141
142 b = list_entry (pos, struct sf_dir_buf, head);
143 if (dir->f_pos >= cur + b->nb_entries) {
144 cur += b->nb_entries;
145 continue;
146 }
147
148 for (i = 0, info = b->buf; i < dir->f_pos - cur; ++i) {
149 size_t size;
150
151 size = offsetof (SHFLDIRINFO, name.String) + info->name.u16Size;
152 info = (SHFLDIRINFO *) ((uintptr_t) info + size);
153 }
154
155 name_ptr = info->name.String.utf8;
156 name_len = info->name.u16Length;
157
158 return sf_nlscpy (sf_g, d_name, NAME_MAX, name_ptr, name_len);
159 }
160 return 1;
161}
162
163/* This is called when vfs wants to populate internal buffers with
164 directory [dir]s contents. [opaque] is an argument to the
165 [filldir]. [filldir] magically modifies it's argument - [opaque]
166 and takes following additional arguments (which i in turn get from
167 the host via sf_getdent):
168
169 name : name of the entry (i must also supply it's length huh?)
170 type : type of the entry (FILE | DIR | etc) (i ellect to use DT_UNKNOWN)
171 pos : position/index of the entry
172 ino : inode number of the entry (i fake those)
173
174 [dir] contains:
175 f_pos : cursor into the directory listing
176 private_data : mean of communcation with the host side
177
178 Extract elements from the directory listing (incrementing f_pos
179 along the way) and feed them to [filldir] until:
180
181 a. there are no more entries (i.e. sf_getdent set done to 1)
182 b. failure to compute fake inode number
183 c. filldir returns an error (see comment on that) */
184static int
185sf_dir_read (struct file *dir, void *opaque, filldir_t filldir)
186{
187 TRACE ();
188 for (;;) {
189 int err;
190 ino_t fake_ino;
191 loff_t sanity;
192 char d_name[NAME_MAX];
193
194 err = sf_getdent (dir, d_name);
195 switch (err) {
196 case 1:
197 return 0;
198
199 case 0:
200 break;
201
202 case -1:
203 default:
204 /* skip erroneous entry and proceed */
205 LogFunc(("sf_getdent error %d\n", err));
206 dir->f_pos += 1;
207 continue;
208 }
209
210 /* d_name now contains valid entry name */
211
212 sanity = dir->f_pos + 0xbeef;
213 fake_ino = sanity;
214 if (sanity - fake_ino) {
215 LogRelFunc(("can not compute ino\n"));
216 return -EINVAL;
217 }
218
219 err = filldir (opaque, d_name, strlen (d_name),
220 dir->f_pos, fake_ino, DT_UNKNOWN);
221 if (err) {
222 LogFunc(("filldir returned error %d\n", err));
223 /* Rely on the fact that filldir returns error
224 only when it runs out of space in opaque */
225 return 0;
226 }
227
228 dir->f_pos += 1;
229 }
230
231 BUG ();
232}
233
234struct file_operations sf_dir_fops = {
235 .open = sf_dir_open,
236 .readdir = sf_dir_read,
237 .release = sf_dir_release,
238 .read = generic_read_dir
239};
240
241
242/* iops */
243
244/* This is called when vfs failed to locate dentry in the cache. The
245 job of this function is to allocate inode and link it to dentry.
246 [dentry] contains the name to be looked in the [parent] directory.
247 Failure to locate the name is not a "hard" error, in this case NULL
248 inode is added to [dentry] and vfs should proceed trying to create
249 the entry via other means. NULL(or "positive" pointer) ought to be
250 returned in case of succes and "negative" pointer on error */
251static struct dentry *
252sf_lookup (struct inode *parent, struct dentry *dentry
253#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
254 , struct nameidata *nd
255#endif
256 )
257{
258 int err;
259 struct sf_inode_info *sf_i, *sf_new_i;
260 struct sf_glob_info *sf_g;
261 SHFLSTRING *path;
262 struct inode *inode;
263 ino_t ino;
264 RTFSOBJINFO fsinfo;
265
266 TRACE ();
267 sf_g = GET_GLOB_INFO (parent->i_sb);
268 sf_i = GET_INODE_INFO (parent);
269
270 BUG_ON (!sf_g);
271 BUG_ON (!sf_i);
272
273 err = sf_path_from_dentry (__func__, sf_g, sf_i, dentry, &path);
274 if (err) {
275 goto fail0;
276 }
277
278 err = sf_stat (__func__, sf_g, path, &fsinfo, 1);
279 if (err) {
280 if (err == -ENOENT) {
281 /* -ENOENT add NULL inode to dentry so it later can be
282 created via call to create/mkdir/open */
283 inode = NULL;
284 }
285 else goto fail1;
286 }
287 else {
288 sf_new_i = kmalloc (sizeof (*sf_new_i), GFP_KERNEL);
289 if (!sf_new_i) {
290 LogRelFunc(("could not allocate memory for new inode info\n"));
291 err = -ENOMEM;
292 goto fail1;
293 }
294 sf_new_i->handle = SHFL_HANDLE_NIL;
295
296 ino = iunique (parent->i_sb, 1);
297#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 25)
298 inode = iget_locked (parent->i_sb, ino);
299#else
300 inode = iget (parent->i_sb, ino);
301#endif
302 if (!inode) {
303 LogFunc(("iget failed\n"));
304 err = -ENOMEM; /* XXX: ??? */
305 goto fail2;
306 }
307
308 SET_INODE_INFO (inode, sf_new_i);
309 sf_init_inode (sf_g, inode, &fsinfo);
310 sf_new_i->path = path;
311
312#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 25)
313 unlock_new_inode(inode);
314#endif
315 }
316
317 sf_i->force_restat = 0;
318 dentry->d_time = jiffies;
319 dentry->d_op = &sf_dentry_ops;
320 d_add (dentry, inode);
321 return NULL;
322
323 fail2:
324 kfree (sf_new_i);
325 fail1:
326 kfree (path);
327 fail0:
328 return ERR_PTR (err);
329}
330
331/* This should allocate memory for sf_inode_info, compute unique inode
332 number, get an inode from vfs, initialize inode info, instantiate
333 dentry */
334static int
335sf_instantiate (const char *caller, struct inode *parent,
336 struct dentry *dentry, SHFLSTRING *path,
337 RTFSOBJINFO *info, SHFLHANDLE handle)
338{
339 int err;
340 ino_t ino;
341 struct inode *inode;
342 struct sf_inode_info *sf_new_i;
343 struct sf_glob_info *sf_g = GET_GLOB_INFO (parent->i_sb);
344
345 TRACE ();
346 BUG_ON (!sf_g);
347
348 sf_new_i = kmalloc (sizeof (*sf_new_i), GFP_KERNEL);
349 if (!sf_new_i) {
350 LogRelFunc(("could not allocate inode info. caller=%s\n", caller));
351 err = -ENOMEM;
352 goto fail0;
353 }
354
355 ino = iunique (parent->i_sb, 1);
356#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 25)
357 inode = iget_locked (parent->i_sb, ino);
358#else
359 inode = iget (parent->i_sb, ino);
360#endif
361 if (!inode) {
362 LogFunc(("iget failed. caller=%s\n", caller));
363 err = -ENOMEM;
364 goto fail1;
365 }
366
367 sf_init_inode (sf_g, inode, info);
368 sf_new_i->path = path;
369 SET_INODE_INFO (inode, sf_new_i);
370
371 dentry->d_time = jiffies;
372 dentry->d_op = &sf_dentry_ops;
373 sf_new_i->force_restat = 1;
374
375 d_instantiate (dentry, inode);
376
377#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 25)
378 unlock_new_inode(inode);
379#endif
380
381 /* Store this handle if we leave the handle open. */
382 sf_new_i->handle = handle;
383 return 0;
384
385 fail1:
386 kfree (sf_new_i);
387 fail0:
388 return err;
389
390}
391
392static int
393sf_create_aux (struct inode *parent, struct dentry *dentry, int mode, int fDirectory)
394{
395 int rc, err;
396 SHFLCREATEPARMS params;
397 SHFLSTRING *path;
398 struct sf_inode_info *sf_i = GET_INODE_INFO (parent);
399 struct sf_glob_info *sf_g = GET_GLOB_INFO (parent->i_sb);
400
401 TRACE ();
402 BUG_ON (!sf_i);
403 BUG_ON (!sf_g);
404
405#if 0
406 printk ("create_aux %s/%s\n", sf_i->path->String.utf8,
407 dentry->d_name.name);
408#endif
409
410 RT_ZERO(params);
411 /* Ensure that the shared folders host service is using our fMode
412 * paramter */
413 params.Handle = SHFL_HANDLE_NIL;
414
415 params.CreateFlags = 0
416 | SHFL_CF_ACT_CREATE_IF_NEW
417 | SHFL_CF_ACT_FAIL_IF_EXISTS
418 | SHFL_CF_ACCESS_READWRITE
419 | (fDirectory ? SHFL_CF_DIRECTORY : 0)
420 ;
421
422 params.Info.Attr.fMode = 0
423 | (fDirectory ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE)
424 | (mode & S_IRWXUGO)
425 ;
426
427 params.Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
428
429 err = sf_path_from_dentry (__func__, sf_g, sf_i, dentry, &path);
430 if (err) {
431 goto fail0;
432 }
433
434 LogFunc(("sf_create_aux: calling vboxCallCreate, folder %s, flags %#x\n",
435 path->String.utf8, params.CreateFlags));
436 rc = vboxCallCreate (&client_handle, &sf_g->map, path, &params);
437 if (RT_FAILURE (rc)) {
438 if (rc == VERR_WRITE_PROTECT) {
439 err = -EROFS;
440 goto fail0;
441 }
442 err = -EPROTO;
443 LogFunc(("(%d): vboxCallCreate(%s) failed rc=%Rrc\n",
444 fDirectory, sf_i->path->String.utf8, rc));
445 goto fail0;
446 }
447
448 if (params.Result != SHFL_FILE_CREATED) {
449 err = -EPERM;
450 LogFunc(("(%d): could not create file %s result=%d\n",
451 fDirectory, sf_i->path->String.utf8, params.Result));
452 goto fail0;
453 }
454
455 err = sf_instantiate (__func__, parent, dentry, path, &params.Info,
456 fDirectory ? SHFL_HANDLE_NIL : params.Handle);
457 if (err) {
458 LogFunc(("(%d): could not instantiate dentry for %s err=%d\n",
459 fDirectory, sf_i->path->String.utf8, err));
460 goto fail1;
461 }
462
463 /*
464 * Don't close this handle right now. We assume that the same file is
465 * opened with sf_reg_open() and later closed with sf_reg_close(). Save
466 * the handle in between. Does not apply to directories. True?
467 */
468 if (fDirectory)
469 {
470 rc = vboxCallClose (&client_handle, &sf_g->map, params.Handle);
471 if (RT_FAILURE (rc)) {
472 LogFunc(("(%d): vboxCallClose failed rc=%Rrc\n", fDirectory, rc));
473 }
474 }
475
476 sf_i->force_restat = 1;
477 return 0;
478
479 fail1:
480 rc = vboxCallClose (&client_handle, &sf_g->map, params.Handle);
481 if (RT_FAILURE (rc)) {
482 LogFunc(("(%d): vboxCallClose failed rc=%Rrc\n", fDirectory, rc));
483 }
484
485 fail0:
486 kfree (path);
487 return err;
488}
489
490static int
491sf_create (struct inode *parent, struct dentry *dentry, int mode
492#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
493 , struct nameidata *nd
494#endif
495 )
496{
497 TRACE ();
498 return sf_create_aux (parent, dentry, mode, 0);
499}
500
501static int
502sf_mkdir (struct inode *parent, struct dentry *dentry, int mode)
503{
504 TRACE ();
505 return sf_create_aux (parent, dentry, mode, 1);
506}
507
508static int
509sf_unlink_aux (struct inode *parent, struct dentry *dentry, int fDirectory)
510{
511 int rc, err;
512 struct sf_glob_info *sf_g = GET_GLOB_INFO (parent->i_sb);
513 struct sf_inode_info *sf_i = GET_INODE_INFO (parent);
514 SHFLSTRING *path;
515
516 TRACE ();
517 BUG_ON (!sf_g);
518
519 err = sf_path_from_dentry (__func__, sf_g, sf_i, dentry, &path);
520 if (err) {
521 goto fail0;
522 }
523
524 rc = vboxCallRemove (&client_handle, &sf_g->map, path,
525 fDirectory ? SHFL_REMOVE_DIR : SHFL_REMOVE_FILE);
526 if (RT_FAILURE (rc)) {
527 LogFunc(("(%d): vboxCallRemove(%s) failed rc=%Rrc\n", fDirectory,
528 path->String.utf8, rc));
529 err = -RTErrConvertToErrno (rc);
530 goto fail1;
531 }
532
533 kfree (path);
534 return 0;
535
536 fail1:
537 kfree (path);
538 fail0:
539 return err;
540}
541
542static int
543sf_unlink (struct inode *parent, struct dentry *dentry)
544{
545 TRACE ();
546 return sf_unlink_aux (parent, dentry, 0);
547}
548
549static int
550sf_rmdir (struct inode *parent, struct dentry *dentry)
551{
552 TRACE ();
553 return sf_unlink_aux (parent, dentry, 1);
554}
555
556static int
557sf_rename (struct inode *old_parent, struct dentry *old_dentry,
558 struct inode *new_parent, struct dentry *new_dentry)
559{
560 int err = 0, rc = VINF_SUCCESS;
561 struct sf_glob_info *sf_g = GET_GLOB_INFO (old_parent->i_sb);
562
563 TRACE ();
564
565 if (sf_g != GET_GLOB_INFO (new_parent->i_sb)) {
566 LogFunc(("rename with different roots\n"));
567 err = -EINVAL;
568 } else {
569 struct sf_inode_info *sf_old_i = GET_INODE_INFO (old_parent);
570 struct sf_inode_info *sf_new_i = GET_INODE_INFO (new_parent);
571 /* As we save the relative path inside the inode structure, we need to change
572 this if the rename is successful. */
573 struct sf_inode_info *sf_file_i = GET_INODE_INFO (old_dentry->d_inode);
574 SHFLSTRING *old_path;
575 SHFLSTRING *new_path;
576
577 BUG_ON (!sf_old_i);
578 BUG_ON (!sf_new_i);
579 BUG_ON (!sf_file_i);
580
581 old_path = sf_file_i->path;
582 err = sf_path_from_dentry (__func__, sf_g, sf_new_i,
583 new_dentry, &new_path);
584 if (err) {
585 LogFunc(("failed to create new path\n"));
586 } else {
587 int is_dir = ((old_dentry->d_inode->i_mode & S_IFDIR) != 0);
588
589 rc = vboxCallRename (&client_handle, &sf_g->map, old_path,
590 new_path, is_dir ? 0 : SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS);
591 if (RT_SUCCESS(rc)) {
592 kfree (old_path);
593 sf_new_i->force_restat = 1;
594 sf_old_i->force_restat = 1; /* XXX: needed? */
595 /* Set the new relative path in the inode. */
596 sf_file_i->path = new_path;
597 } else {
598 LogFunc(("vboxCallRename failed rc=%Rrc\n", rc));
599 err = -RTErrConvertToErrno (rc);
600 }
601 if (0 != err)
602 kfree (new_path);
603 }
604 }
605 return err;
606}
607
608struct inode_operations sf_dir_iops = {
609 .lookup = sf_lookup,
610 .create = sf_create,
611 .mkdir = sf_mkdir,
612 .rmdir = sf_rmdir,
613 .unlink = sf_unlink,
614 .rename = sf_rename,
615#if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
616 .revalidate = sf_inode_revalidate
617#else
618 .getattr = sf_getattr,
619 .setattr = sf_setattr
620#endif
621};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette