VirtualBox

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

Last change on this file since 4808 was 4808, checked in by vboxsync, 18 years ago

Added an RTErrnoConvertFromErr function to simplify error management inside the Linux Additions kernel modules

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.2 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 err = -RTErrnoConvertFromErr(rc);
494 goto fail1;
495 }
496
497 kfree (path);
498 return 0;
499
500 fail1:
501 kfree (path);
502 fail0:
503 return err;
504}
505
506static int
507sf_unlink (struct inode *parent, struct dentry *dentry)
508{
509 TRACE ();
510 return sf_unlink_aux (parent, dentry, 0);
511}
512
513static int
514sf_rmdir (struct inode *parent, struct dentry *dentry)
515{
516 TRACE ();
517 return sf_unlink_aux (parent, dentry, 1);
518}
519
520static int
521sf_rename (struct inode *old_parent, struct dentry *old_dentry,
522 struct inode *new_parent, struct dentry *new_dentry)
523{
524 int err = 0, rc = VINF_SUCCESS;
525 struct sf_glob_info *sf_g = GET_GLOB_INFO (old_parent->i_sb);
526
527 TRACE ();
528
529 if (sf_g != GET_GLOB_INFO (new_parent->i_sb)) {
530 LogFunc(("rename with different roots\n"));
531 err = -EINVAL;
532 } else {
533 struct sf_inode_info *sf_old_i = GET_INODE_INFO (old_parent);
534 struct sf_inode_info *sf_new_i = GET_INODE_INFO (new_parent);
535 /* As we save the relative path inside the inode structure, we need to change
536 this if the rename is successful. */
537 struct sf_inode_info *sf_file_i = GET_INODE_INFO (old_dentry->d_inode);
538 SHFLSTRING *old_path;
539 SHFLSTRING *new_path;
540
541 BUG_ON (!sf_old_i);
542 BUG_ON (!sf_new_i);
543 BUG_ON (!sf_file_i);
544
545 old_path = sf_file_i->path;
546 err = sf_path_from_dentry (__func__, sf_g, sf_new_i,
547 new_dentry, &new_path);
548 if (err) {
549 LogFunc(("failed to create new path\n"));
550 } else {
551 int is_dir = ((old_dentry->d_inode->i_mode & S_IFDIR) != 0);
552
553 rc = vboxCallRename (&client_handle, &sf_g->map, old_path,
554 new_path, is_dir ? 0 : SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS);
555 if (RT_SUCCESS(rc)) {
556 kfree (old_path);
557 sf_new_i->force_restat = 1;
558 sf_old_i->force_restat = 1; /* XXX: needed? */
559 /* Set the new relative path in the inode. */
560 sf_file_i->path = new_path;
561 } else {
562 LogFunc(("vboxCallRename failed rc=%Vrc\n", rc));
563 err = -RTErrnoConvertFromErr(err);
564 }
565 if (0 != err) {
566 kfree (new_path);
567 }
568 }
569 }
570 return err;
571}
572
573struct inode_operations sf_dir_iops = {
574 .lookup = sf_lookup,
575 .create = sf_create,
576 .mkdir = sf_mkdir,
577 .rmdir = sf_rmdir,
578 .unlink = sf_unlink,
579 .rename = sf_rename,
580#if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
581 .revalidate = sf_inode_revalidate
582#else
583 .getattr = sf_getattr
584#endif
585};
Note: See TracBrowser for help on using the repository browser.

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