VirtualBox

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

Last change on this file since 4743 was 4743, checked in by vboxsync, 17 years ago

Fix for when open is called on a Linux guest shared folder with O_CREAT|O_EXCL

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