VirtualBox

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

Last change on this file since 11507 was 11507, checked in by vboxsync, 16 years ago

Linux guest additions: allow to specify dmode, fmode, umask, dmask, fmask when mounting a shared folder. Note: read/write permissions currently not checked by vboxvfs.

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