VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/SharedFolders/vboxfs_vnode.c@ 35678

Last change on this file since 35678 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 42.9 KB
Line 
1/** @file
2 * VirtualBox File System for Solaris Guests, vnode implementation.
3 * Portions contributed by: Ronald.
4 */
5
6/*
7 * Copyright (C) 2009-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*
28 * Shared Folder File System is used from Solaris when run as a guest operating
29 * system on VirtualBox, though is meant to be usable with any hypervisor that
30 * can provide similar functionality. The sffs code handles all the Solaris
31 * specific semantics and relies on a provider module to actually access
32 * directories, files, etc. The provider interfaces are described in
33 * "vboxfs_prov.h" and the module implementing them is shipped as part of the
34 * VirtualBox Guest Additions for Solaris.
35 *
36 * The shared folder file system is similar to a networked file system,
37 * but with some caveats. The sffs code caches minimal information and proxies
38 * out to the provider whenever possible. Here are some things that are
39 * handled in this code and not by the proxy:
40 *
41 * - a way to open ".." from any already open directory
42 * - st_ino numbers
43 * - detecting directory changes that happened on the host.
44 *
45 * The implementation builds a cache of information for every file/directory
46 * ever accessed in all mounted sffs filesystems using sf_node structures.
47 *
48 * This information for both open or closed files can become invalid if
49 * asynchronous changes are made on the host. Solaris should not panic() in
50 * this event, but some file system operations may return unexpected errors.
51 * Information for such directories or files while they have active vnodes
52 * is removed from the regular cache and stored in a "stale" bucket until
53 * the vnode becomes completely inactive.
54 *
55 * No file data is cached in the guest. This means we don't support mmap() yet.
56 * A future version could relatively easily add support for read-only
57 * mmap(MAP_SHARED) and any mmap(MAP_PRIVATE). But a la ZFS, this data caching
58 * would not be coherent with normal simultaneous read()/write() operations,
59 * nor will it be coherent with data access on the host. Writable
60 * mmap(MAP_SHARED) access is possible, but guaranteeing any kind of coherency
61 * with concurrent activity on the host would be near impossible with the
62 * existing interfaces.
63 *
64 * A note about locking. sffs is not a high performance file system.
65 * No fine grained locking is done. The one sffs_lock protects just about
66 * everything.
67 */
68
69#include <VBox/log.h>
70
71#include <unistd.h>
72#include <sys/types.h>
73#include <sys/stat.h>
74#include <sys/mntent.h>
75#include <sys/param.h>
76#include <sys/modctl.h>
77#include <sys/mount.h>
78#include <sys/policy.h>
79#include <sys/atomic.h>
80#include <sys/sysmacros.h>
81#include <sys/ddi.h>
82#include <sys/sunddi.h>
83#include <sys/vfs.h>
84#if !defined(VBOX_VFS_SOLARIS_10U6)
85#include <sys/vfs_opreg.h>
86#endif
87#include <sys/pathname.h>
88#include <sys/dirent.h>
89#include <sys/fs_subr.h>
90#include <sys/time.h>
91#include "vboxfs_prov.h"
92#include "vboxfs_vnode.h"
93#include "vboxfs_vfs.h"
94
95static struct vnodeops *sffs_ops = NULL;
96
97kmutex_t sffs_lock;
98static avl_tree_t sfnodes;
99static avl_tree_t stale_sfnodes;
100
101/*
102 * For now we'll use an I/O buffer that doesn't page fault for VirtualBox
103 * to transfer data into.
104 */
105char *sffs_buffer;
106
107/*
108 * sfnode_compare() is needed for AVL tree functionality.
109 * The nodes are sorted by mounted filesystem, then path. If the
110 * nodes are stale, the node pointer itself is used to force uniqueness.
111 */
112static int
113sfnode_compare(const void *a, const void *b)
114{
115 sfnode_t *x = (sfnode_t *)a;
116 sfnode_t *y = (sfnode_t *)b;
117 int diff;
118
119 if (x->sf_is_stale) {
120 ASSERT(y->sf_is_stale);
121 diff = strcmp(x->sf_path, y->sf_path);
122 if (diff == 0)
123 diff = (uintptr_t)y - (uintptr_t)x;
124 } else {
125 ASSERT(!y->sf_is_stale);
126 diff = (uintptr_t)y->sf_sffs - (uintptr_t)x->sf_sffs;
127 if (diff == 0)
128 diff = strcmp(x->sf_path, y->sf_path);
129 }
130 if (diff < 0)
131 return (-1);
132 if (diff > 0)
133 return (1);
134 return (0);
135}
136
137/*
138 * Construct a new pathname given an sfnode plus an optional tail component.
139 * This handles ".." and "."
140 */
141static char *
142sfnode_construct_path(sfnode_t *node, char *tail)
143{
144 char *p;
145
146 if (strcmp(tail, ".") == 0 || strcmp(tail, "..") == 0)
147 panic("construct path for %s", tail);
148 p = kmem_alloc(strlen(node->sf_path) + 1 + strlen(tail) + 1, KM_SLEEP);
149 strcpy(p, node->sf_path);
150 strcat(p, "/");
151 strcat(p, tail);
152 return (p);
153}
154
155/*
156 * Clears the (cached) directory listing for the node.
157 */
158static void
159sfnode_clear_dir_list(sfnode_t *node)
160{
161 ASSERT(MUTEX_HELD(&sffs_lock));
162
163 while (node->sf_dir_list != NULL) {
164 sffs_dirents_t *next = node->sf_dir_list->sf_next;
165 kmem_free(node->sf_dir_list, SFFS_DIRENTS_SIZE);
166 node->sf_dir_list = next;
167 }
168
169 while (node->sf_dir_stats != NULL) {
170 sffs_stats_t *next = node->sf_dir_stats->sf_next;
171 kmem_free(node->sf_dir_stats, sizeof(*node->sf_dir_stats));
172 node->sf_dir_stats = next;
173 }
174}
175
176/*
177 * Open the provider file associated with a vnode. Holding the file open is
178 * the only way we have of trying to have a vnode continue to refer to the
179 * same host file in the host in light of the possibility of host side renames.
180 */
181static void
182sfnode_open(sfnode_t *node)
183{
184 int error;
185 sfp_file_t *fp;
186
187 if (node->sf_file != NULL)
188 return;
189 error = sfprov_open(node->sf_sffs->sf_handle, node->sf_path, &fp);
190 if (error == 0)
191 node->sf_file = fp;
192}
193
194/*
195 * get a new vnode reference for an sfnode
196 */
197vnode_t *
198sfnode_get_vnode(sfnode_t *node)
199{
200 vnode_t *vp;
201
202 if (node->sf_vnode != NULL) {
203 VN_HOLD(node->sf_vnode);
204 } else {
205 vp = vn_alloc(KM_SLEEP);
206 LogFlowFunc((" %s gets vnode 0x%p\n", node->sf_path, vp));
207 vp->v_type = node->sf_type;
208 vp->v_vfsp = node->sf_sffs->sf_vfsp;
209 VFS_HOLD(vp->v_vfsp);
210 vn_setops(vp, sffs_ops);
211 vp->v_flag = VNOMAP | VNOSWAP;
212 vn_exists(vp);
213 vp->v_data = node;
214 node->sf_vnode = vp;
215 }
216 sfnode_open(node);
217 return (node->sf_vnode);
218}
219
220/*
221 * Allocate and initialize a new sfnode and assign it a vnode
222 */
223sfnode_t *
224sfnode_make(
225 sffs_data_t *sffs,
226 char *path,
227 vtype_t type,
228 sfp_file_t *fp,
229 sfnode_t *parent, /* can be NULL for root */
230 sffs_stat_t *stat,
231 uint64_t stat_time)
232{
233 sfnode_t *node;
234 avl_index_t where;
235
236 ASSERT(MUTEX_HELD(&sffs_lock));
237 ASSERT(path != NULL);
238
239 /*
240 * build the sfnode
241 */
242 LogFlowFunc(("sffs_make(%s)\n", path));
243 node = kmem_alloc(sizeof (*node), KM_SLEEP);
244 node->sf_sffs = sffs;
245 node->sf_path = path;
246 node->sf_ino = sffs->sf_ino++;
247 node->sf_type = type;
248 node->sf_is_stale = 0; /* never stale at creation */
249 node->sf_file = fp;
250 node->sf_vnode = NULL; /* do this before any sfnode_get_vnode() */
251 node->sf_children = 0;
252 node->sf_parent = parent;
253 if (parent)
254 ++parent->sf_children;
255 node->sf_dir_list = NULL;
256 node->sf_dir_stats = NULL;
257 if (stat != NULL) {
258 node->sf_stat = *stat;
259 node->sf_stat_time = stat_time;
260 } else {
261 node->sf_stat_time = 0;
262 }
263
264 /*
265 * add the new node to our cache
266 */
267 if (avl_find(&sfnodes, node, &where) != NULL)
268 panic("sffs_create_sfnode(%s): duplicate sfnode_t", path);
269 avl_insert(&sfnodes, node, where);
270 return (node);
271}
272
273/*
274 * destroy an sfnode
275 */
276static void
277sfnode_destroy(sfnode_t *node)
278{
279 avl_index_t where;
280 avl_tree_t *tree;
281 sfnode_t *parent;
282top:
283 parent = node->sf_parent;
284 ASSERT(MUTEX_HELD(&sffs_lock));
285 ASSERT(node->sf_path != NULL);
286 LogFlowFunc(("sffs_destroy(%s)%s\n", node->sf_path, node->sf_is_stale ? " stale": ""));
287 if (node->sf_children != 0)
288 panic("sfnode_destroy(%s) has %d children", node->sf_path, node->sf_children);
289 if (node->sf_vnode != NULL)
290 panic("sfnode_destroy(%s) has active vnode", node->sf_path);
291
292 if (node->sf_is_stale)
293 tree = &stale_sfnodes;
294 else
295 tree = &sfnodes;
296 if (avl_find(tree, node, &where) == NULL)
297 panic("sfnode_destroy(%s) not found", node->sf_path);
298 avl_remove(tree, node);
299
300 VFS_RELE(node->sf_sffs->sf_vfsp);
301 sfnode_clear_dir_list(node);
302 kmem_free(node->sf_path, strlen(node->sf_path) + 1);
303 kmem_free(node, sizeof (*node));
304 if (parent != NULL) {
305 sfnode_clear_dir_list(parent);
306 if (parent->sf_children == 0)
307 panic("sfnode_destroy(%s) parent has no child", node->sf_path);
308 --parent->sf_children;
309 if (parent->sf_children == 0 &&
310 parent->sf_is_stale &&
311 parent->sf_vnode == NULL) {
312 node = parent;
313 goto top;
314 }
315 }
316}
317
318/*
319 * Some sort of host operation on an sfnode has failed or it has been
320 * deleted. Mark this node and any children as stale, deleting knowledge
321 * about any which do not have active vnodes or children
322 * This also handle deleting an inactive node that was already stale.
323 */
324static void
325sfnode_make_stale(sfnode_t *node)
326{
327 sfnode_t *n;
328 int len;
329 ASSERT(MUTEX_HELD(&sffs_lock));
330 avl_index_t where;
331
332 /*
333 * First deal with any children of a directory node.
334 * If a directory becomes stale, anything below it becomes stale too.
335 */
336 if (!node->sf_is_stale && node->sf_type == VDIR) {
337 len = strlen(node->sf_path);
338
339 n = node;
340 while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
341 ASSERT(!n->sf_is_stale);
342
343 /*
344 * quit when no longer seeing children of node
345 */
346 if (n->sf_sffs != node->sf_sffs ||
347 strncmp(node->sf_path, n->sf_path, len) != 0 ||
348 n->sf_path[len] != '/')
349 break;
350
351 /*
352 * Either mark the child as stale or destroy it
353 */
354 if (n->sf_vnode == NULL && n->sf_children == 0) {
355 sfnode_destroy(n);
356 } else {
357 LogFlowFunc(("sffs_make_stale(%s) sub\n", n->sf_path));
358 sfnode_clear_dir_list(n);
359 if (avl_find(&sfnodes, n, &where) == NULL)
360 panic("sfnode_make_stale(%s)"
361 " not in sfnodes", n->sf_path);
362 avl_remove(&sfnodes, n);
363 n->sf_is_stale = 1;
364 if (avl_find(&stale_sfnodes, n, &where) != NULL)
365 panic("sffs_make_stale(%s) duplicates",
366 n->sf_path);
367 avl_insert(&stale_sfnodes, n, where);
368 }
369 }
370 }
371
372 /*
373 * Now deal with the given node.
374 */
375 if (node->sf_vnode == NULL && node->sf_children == 0) {
376 sfnode_destroy(node);
377 } else if (!node->sf_is_stale) {
378 LogFlowFunc(("sffs_make_stale(%s)\n", node->sf_path));
379 sfnode_clear_dir_list(node);
380 if (node->sf_parent)
381 sfnode_clear_dir_list(node->sf_parent);
382 if (avl_find(&sfnodes, node, &where) == NULL)
383 panic("sfnode_make_stale(%s) not in sfnodes",
384 node->sf_path);
385 avl_remove(&sfnodes, node);
386 node->sf_is_stale = 1;
387 if (avl_find(&stale_sfnodes, node, &where) != NULL)
388 panic("sffs_make_stale(%s) duplicates", node->sf_path);
389 avl_insert(&stale_sfnodes, node, where);
390 }
391}
392
393static uint64_t
394sfnode_cur_time_usec(void)
395{
396 timestruc_t now = hrestime;
397 return (now.tv_sec * 1000000L + now.tv_nsec / 1000L);
398}
399
400static int
401sfnode_stat_cached(sfnode_t *node)
402{
403 return (sfnode_cur_time_usec() - node->sf_stat_time) <
404 node->sf_sffs->sf_stat_ttl * 1000L;
405}
406
407static int
408sfnode_get_stat(sfp_mount_t *mnt, char *path, sffs_stat_t *stat)
409{
410 return sfprov_get_attr(mnt, path, &stat->sf_mode, &stat->sf_size,
411 &stat->sf_atime, &stat->sf_mtime, &stat->sf_ctime);
412}
413
414static void
415sfnode_invalidate_stat_cache(sfnode_t *node)
416{
417 node->sf_stat_time = 0;
418}
419
420static int
421sfnode_update_stat_cache(sfnode_t *node)
422{
423 int error;
424
425 error = sfnode_get_stat(node->sf_sffs->sf_handle, node->sf_path,
426 &node->sf_stat);
427 if (error == ENOENT)
428 sfnode_make_stale(node);
429 if (error == 0)
430 node->sf_stat_time = sfnode_cur_time_usec();
431
432 return (error);
433}
434
435/*
436 * Rename a file or a directory
437 */
438static void
439sfnode_rename(sfnode_t *node, sfnode_t *newparent, char *path)
440{
441 sfnode_t *n;
442 sfnode_t template;
443 avl_index_t where;
444 int len = strlen(path);
445 int old_len;
446 char *new_path;
447 char *tail;
448 ASSERT(MUTEX_HELD(&sffs_lock));
449
450 ASSERT(!node->sf_is_stale);
451
452 /*
453 * Have to remove anything existing that had the new name.
454 */
455 template.sf_sffs = node->sf_sffs;
456 template.sf_path = path;
457 template.sf_is_stale = 0;
458 n = avl_find(&sfnodes, &template, &where);
459 if (n != NULL)
460 sfnode_make_stale(n);
461
462 /*
463 * Do the renaming, deal with any children of this node first.
464 */
465 if (node->sf_type == VDIR) {
466 old_len = strlen(node->sf_path);
467 while ((n = AVL_NEXT(&sfnodes, node)) != NULL) {
468
469 /*
470 * quit when no longer seeing children of node
471 */
472 if (n->sf_sffs != node->sf_sffs ||
473 strncmp(node->sf_path, n->sf_path, old_len) != 0 ||
474 n->sf_path[old_len] != '/')
475 break;
476
477 /*
478 * Rename the child:
479 * - build the new path name
480 * - unlink the AVL node
481 * - assign the new name
482 * - re-insert the AVL name
483 */
484 ASSERT(strlen(n->sf_path) > old_len);
485 tail = n->sf_path + old_len; /* includes initial "/" */
486 new_path = kmem_alloc(len + strlen(tail) + 1,
487 KM_SLEEP);
488 strcpy(new_path, path);
489 strcat(new_path, tail);
490 if (avl_find(&sfnodes, n, &where) == NULL)
491 panic("sfnode_rename(%s) not in sfnodes",
492 n->sf_path);
493 avl_remove(&sfnodes, n);
494 LogFlowFunc(("sfnode_rname(%s to %s) sub\n", n->sf_path, new_path));
495 kmem_free(n->sf_path, strlen(n->sf_path) + 1);
496 n->sf_path = new_path;
497 if (avl_find(&sfnodes, n, &where) != NULL)
498 panic("sfnode_rename(%s) duplicates",
499 n->sf_path);
500 avl_insert(&sfnodes, n, where);
501 }
502 }
503
504 /*
505 * Deal with the given node.
506 */
507 if (avl_find(&sfnodes, node, &where) == NULL)
508 panic("sfnode_rename(%s) not in sfnodes", node->sf_path);
509 avl_remove(&sfnodes, node);
510 LogFlowFunc(("sfnode_rname(%s to %s)\n", node->sf_path, path));
511 kmem_free(node->sf_path, strlen(node->sf_path) + 1);
512 node->sf_path = path;
513 if (avl_find(&sfnodes, node, &where) != NULL)
514 panic("sfnode_rename(%s) duplicates", node->sf_path);
515 avl_insert(&sfnodes, node, where);
516
517 /*
518 * change the parent
519 */
520 if (node->sf_parent == NULL)
521 panic("sfnode_rename(%s) no parent", node->sf_path);
522 if (node->sf_parent->sf_children == 0)
523 panic("sfnode_rename(%s) parent has no child", node->sf_path);
524 sfnode_clear_dir_list(node->sf_parent);
525 sfnode_clear_dir_list(newparent);
526 --node->sf_parent->sf_children;
527 node->sf_parent = newparent;
528 ++newparent->sf_children;
529}
530
531/*
532 * Look for a cached node, if not found either handle ".." or try looking
533 * via the provider. Create an entry in sfnodes if found but not cached yet.
534 * If the create flag is set, a file or directory is created. If the file
535 * already existed, an error is returned.
536 * Nodes returned from this routine always have a vnode with its ref count
537 * bumped by 1.
538 */
539static sfnode_t *
540sfnode_lookup(
541 sfnode_t *dir,
542 char *name,
543 vtype_t create,
544 sffs_stat_t *stat,
545 uint64_t stat_time,
546 int *err)
547{
548 avl_index_t where;
549 sfnode_t template;
550 sfnode_t *node;
551 int error = 0;
552 int type;
553 char *fullpath;
554 sfp_file_t *fp;
555 sffs_stat_t tmp_stat;
556
557 ASSERT(MUTEX_HELD(&sffs_lock));
558
559 if (err)
560 *err = error;
561
562 /*
563 * handle referencing myself
564 */
565 if (strcmp(name, "") == 0 || strcmp(name, ".") == 0)
566 return (dir);
567
568 /*
569 * deal with parent
570 */
571 if (strcmp(name, "..") == 0)
572 return (dir->sf_parent);
573
574 /*
575 * Look for an existing node.
576 */
577 fullpath = sfnode_construct_path(dir, name);
578 template.sf_sffs = dir->sf_sffs;
579 template.sf_path = fullpath;
580 template.sf_is_stale = 0;
581 node = avl_find(&sfnodes, &template, &where);
582 if (node != NULL) {
583 kmem_free(fullpath, strlen(fullpath) + 1);
584 if (create != VNON)
585 return (NULL);
586 return (node);
587 }
588
589 /*
590 * No entry for this path currently.
591 * Check if the file exists with the provider and get the type from
592 * there.
593 */
594 if (create == VREG) {
595 type = VREG;
596 error = sfprov_create(dir->sf_sffs->sf_handle, fullpath, &fp);
597 } else if (create == VDIR) {
598 type = VDIR;
599 error = sfprov_mkdir(dir->sf_sffs->sf_handle, fullpath, &fp);
600 } else {
601 mode_t m;
602 fp = NULL;
603 type = VNON;
604 if (stat == NULL) {
605 stat = &tmp_stat;
606 error = sfnode_get_stat(dir->sf_sffs->sf_handle,
607 fullpath, stat);
608 stat_time = sfnode_cur_time_usec();
609 } else {
610 error = 0;
611 }
612 m = stat->sf_mode;
613 if (error != 0)
614 error = ENOENT;
615 else if (S_ISDIR(m))
616 type = VDIR;
617 else if (S_ISREG(m))
618 type = VREG;
619 }
620
621 if (err)
622 *err = error;
623
624 /*
625 * If no errors, make a new node and return it.
626 */
627 if (error) {
628 kmem_free(fullpath, strlen(fullpath) + 1);
629 return (NULL);
630 }
631 node = sfnode_make(dir->sf_sffs, fullpath, type, fp, dir, stat,
632 stat_time);
633 return (node);
634}
635
636
637/*
638 * uid and gid in sffs determine owner and group for all files.
639 */
640static int
641sfnode_access(sfnode_t *node, mode_t mode, cred_t *cr)
642{
643 sffs_data_t *sffs = node->sf_sffs;
644 mode_t m;
645 int shift = 0;
646 int error;
647 vnode_t *vp;
648
649 ASSERT(MUTEX_HELD(&sffs_lock));
650
651 /*
652 * get the mode from the cache or provider
653 */
654 if (sfnode_stat_cached(node))
655 error = 0;
656 else
657 error = sfnode_update_stat_cache(node);
658 m = (error == 0) ? node->sf_stat.sf_mode : 0;
659
660 /*
661 * mask off the permissions based on uid/gid
662 */
663 if (crgetuid(cr) != sffs->sf_uid) {
664 shift += 3;
665 if (groupmember(sffs->sf_gid, cr) == 0)
666 shift += 3;
667 }
668 mode &= ~(m << shift);
669
670 if (mode == 0) {
671 error = 0;
672 } else {
673 vp = sfnode_get_vnode(node);
674 error = secpolicy_vnode_access(cr, vp, sffs->sf_uid, mode);
675 VN_RELE(vp);
676 }
677 return (error);
678}
679
680
681/*
682 *
683 * Everything below this point are the vnode operations used by Solaris VFS
684 */
685static int
686sffs_readdir(
687 vnode_t *vp,
688 uio_t *uiop,
689 cred_t *cred,
690 int *eofp,
691 caller_context_t *ct,
692 int flags)
693{
694 sfnode_t *dir = VN2SFN(vp);
695 sfnode_t *node;
696 struct dirent64 *dirent;
697 sffs_dirents_t *cur_buf;
698 sffs_stats_t *cur_stats;
699 int cur_snum;
700 offset_t offset;
701 int dummy_eof;
702 int error = 0;
703
704 if (uiop->uio_iovcnt != 1)
705 return (EINVAL);
706
707 if (vp->v_type != VDIR)
708 return (ENOTDIR);
709
710 if (eofp == NULL)
711 eofp = &dummy_eof;
712 *eofp = 0;
713
714 if (uiop->uio_loffset >= MAXOFFSET_T) {
715 *eofp = 1;
716 return (0);
717 }
718
719 /*
720 * Get the directory entry names from the host. This gets all
721 * entries. These are stored in a linked list of sffs_dirents_t
722 * buffers, each of which contains a list of dirent64_t's.
723 */
724 mutex_enter(&sffs_lock);
725
726 if (dir->sf_dir_list == NULL) {
727 error = sfprov_readdir(dir->sf_sffs->sf_handle, dir->sf_path,
728 &dir->sf_dir_list, &dir->sf_dir_stats);
729 if (error != 0)
730 goto done;
731 }
732
733 /*
734 * Lookup each of the names, so that we have ino's, and copy to
735 * result buffer.
736 */
737 offset = 0;
738 cur_buf = dir->sf_dir_list;
739 cur_stats = dir->sf_dir_stats;
740 cur_snum = 0;
741 while (cur_buf != NULL) {
742 if (offset + cur_buf->sf_len <= uiop->uio_loffset) {
743 offset += cur_buf->sf_len;
744 cur_buf = cur_buf->sf_next;
745 continue;
746 }
747
748 if (cur_snum >= SFFS_STATS_LEN) {
749 cur_stats = cur_stats->sf_next;
750 cur_snum = 0;
751 }
752
753 dirent = (dirent64_t *)
754 (((char *) &cur_buf->sf_entries[0]) +
755 (uiop->uio_loffset - offset));
756 if (dirent->d_reclen > uiop->uio_resid)
757 break;
758
759 if (strcmp(dirent->d_name, ".") == 0) {
760 node = dir;
761 } else if (strcmp(dirent->d_name, "..") == 0) {
762 node = dir->sf_parent;
763 if (node == NULL)
764 node = dir;
765 } else {
766 node = sfnode_lookup(dir, dirent->d_name, VNON,
767 &cur_stats->sf_stats[cur_snum],
768 sfnode_cur_time_usec(), NULL);
769 if (node == NULL)
770 panic("sffs_readdir() lookup failed");
771 }
772 dirent->d_ino = node->sf_ino;
773
774 error = uiomove(dirent, dirent->d_reclen, UIO_READ, uiop);
775 ++cur_snum;
776 if (error != 0)
777 break;
778 }
779 if (error == 0 && cur_buf == NULL)
780 *eofp = 1;
781done:
782 mutex_exit(&sffs_lock);
783 return (error);
784}
785
786
787#if defined(VBOX_VFS_SOLARIS_10U6)
788/*
789 * HERE JOE.. this may need more logic, need to look at other file systems
790 */
791static int
792sffs_pathconf(
793 vnode_t *vp,
794 int cmd,
795 ulong_t *valp,
796 cred_t *cr)
797{
798 return (fs_pathconf(vp, cmd, valp, cr));
799}
800#else
801/*
802 * HERE JOE.. this may need more logic, need to look at other file systems
803 */
804static int
805sffs_pathconf(
806 vnode_t *vp,
807 int cmd,
808 ulong_t *valp,
809 cred_t *cr,
810 caller_context_t *ct)
811{
812 return (fs_pathconf(vp, cmd, valp, cr, ct));
813}
814#endif
815
816static int
817sffs_getattr(
818 vnode_t *vp,
819 vattr_t *vap,
820 int flags,
821 cred_t *cred,
822 caller_context_t *ct)
823{
824 sfnode_t *node = VN2SFN(vp);
825 sffs_data_t *sffs = node->sf_sffs;
826 mode_t mode;
827 int error = 0;
828
829 mutex_enter(&sffs_lock);
830 vap->va_type = vp->v_type;
831 vap->va_uid = sffs->sf_uid;
832 vap->va_gid = sffs->sf_gid;
833 vap->va_fsid = sffs->sf_vfsp->vfs_dev;
834 vap->va_nodeid = node->sf_ino;
835 vap->va_nlink = 1;
836 vap->va_rdev = sffs->sf_vfsp->vfs_dev;
837 vap->va_seq = 0;
838
839 if (!sfnode_stat_cached(node)) {
840 error = sfnode_update_stat_cache(node);
841 if (error != 0)
842 goto done;
843 }
844
845 vap->va_atime = node->sf_stat.sf_atime;
846 vap->va_mtime = node->sf_stat.sf_mtime;
847 vap->va_ctime = node->sf_stat.sf_ctime;
848
849 mode = node->sf_stat.sf_mode;
850 vap->va_mode = mode & MODEMASK;
851 if (S_ISDIR(mode))
852 vap->va_type = VDIR;
853 else if (S_ISREG(mode))
854 vap->va_type = VREG;
855 else if (S_ISFIFO(mode))
856 vap->va_type = VFIFO;
857 else if (S_ISCHR(mode))
858 vap->va_type = VCHR;
859 else if (S_ISBLK(mode))
860 vap->va_type = VBLK;
861 else if (S_ISLNK(mode))
862 vap->va_type = VLNK;
863 else if (S_ISSOCK(mode))
864 vap->va_type = VSOCK;
865
866 vap->va_size = node->sf_stat.sf_size;
867 vap->va_blksize = 512;
868 vap->va_nblocks = (vap->va_size + 511) / 512;
869
870done:
871 mutex_exit(&sffs_lock);
872 return (error);
873}
874
875static int
876sffs_setattr(
877 vnode_t *vp,
878 vattr_t *vap,
879 int flags,
880 cred_t *cred,
881 caller_context_t *ct)
882{
883 sfnode_t *node = VN2SFN(vp);
884 int error;
885 mode_t mode;
886
887 mode = vap->va_mode;
888 if (vp->v_type == VREG)
889 mode |= S_IFREG;
890 else if (vp->v_type == VDIR)
891 mode |= S_IFDIR;
892 else if (vp->v_type == VBLK)
893 mode |= S_IFBLK;
894 else if (vp->v_type == VCHR)
895 mode |= S_IFCHR;
896 else if (vp->v_type == VLNK)
897 mode |= S_IFLNK;
898 else if (vp->v_type == VFIFO)
899 mode |= S_IFIFO;
900 else if (vp->v_type == VSOCK)
901 mode |= S_IFSOCK;
902
903 mutex_enter(&sffs_lock);
904
905 sfnode_invalidate_stat_cache(node);
906 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
907 vap->va_mask, mode, vap->va_atime, vap->va_mtime, vap->va_ctime);
908 if (error == ENOENT)
909 sfnode_make_stale(node);
910
911 mutex_exit(&sffs_lock);
912 return (error);
913}
914
915static int
916sffs_space(
917 vnode_t *vp,
918 int cmd,
919 struct flock64 *bfp,
920 int flags,
921 offset_t off,
922 cred_t *cred,
923 caller_context_t *ct)
924{
925 sfnode_t *node = VN2SFN(vp);
926 int error;
927
928 /* we only support changing the length of the file */
929 if (bfp->l_whence != SEEK_SET || bfp->l_len != 0)
930 return ENOSYS;
931
932 mutex_enter(&sffs_lock);
933
934 sfnode_invalidate_stat_cache(node);
935
936 error = sfprov_set_size(node->sf_sffs->sf_handle, node->sf_path,
937 bfp->l_start);
938 if (error == ENOENT)
939 sfnode_make_stale(node);
940
941 mutex_exit(&sffs_lock);
942 return (error);
943}
944
945/*ARGSUSED*/
946static int
947sffs_read(
948 vnode_t *vp,
949 struct uio *uio,
950 int ioflag,
951 cred_t *cred,
952 caller_context_t *ct)
953{
954 sfnode_t *node = VN2SFN(vp);
955 int error = 0;
956 uint32_t bytes;
957 uint32_t done;
958 ulong_t offset;
959 ssize_t total;
960
961 if (vp->v_type == VDIR)
962 return (EISDIR);
963 if (vp->v_type != VREG)
964 return (EINVAL);
965 if (uio->uio_loffset >= MAXOFFSET_T)
966 {
967 proc_t *p = ttoproc(curthread);
968 mutex_enter(&p->p_lock);
969 (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
970 p, RCA_UNSAFE_SIGINFO);
971 mutex_exit(&p->p_lock);
972 return (EFBIG);
973 }
974 if (uio->uio_loffset < 0)
975 return (EINVAL);
976 total = uio->uio_resid;
977 if (total == 0)
978 return (0);
979
980 mutex_enter(&sffs_lock);
981 sfnode_open(node);
982 if (node->sf_file == NULL) {
983 mutex_exit(&sffs_lock);
984 return (EINVAL);
985 }
986
987 do {
988 offset = uio->uio_offset;
989 done = bytes = MIN(PAGESIZE, uio->uio_resid);
990 error = sfprov_read(node->sf_file, sffs_buffer, offset, &done);
991 if (error == 0 && done > 0)
992 error = uiomove(sffs_buffer, done, UIO_READ, uio);
993 } while (error == 0 && uio->uio_resid > 0 && done > 0);
994
995 mutex_exit(&sffs_lock);
996
997 /*
998 * a partial read is never an error
999 */
1000 if (total != uio->uio_resid)
1001 error = 0;
1002 return (error);
1003}
1004
1005/*ARGSUSED*/
1006static int
1007sffs_write(
1008 vnode_t *vp,
1009 struct uio *uiop,
1010 int ioflag,
1011 cred_t *cred,
1012 caller_context_t *ct)
1013{
1014 sfnode_t *node = VN2SFN(vp);
1015 int error = 0;
1016 uint32_t bytes;
1017 uint32_t done;
1018 ulong_t offset;
1019 ssize_t total;
1020 rlim64_t limit = uiop->uio_llimit;
1021
1022 if (vp->v_type == VDIR)
1023 return (EISDIR);
1024 if (vp->v_type != VREG)
1025 return (EINVAL);
1026
1027 /*
1028 * We have to hold this lock for a long time to keep
1029 * multiple FAPPEND writes from intermixing
1030 */
1031 mutex_enter(&sffs_lock);
1032 sfnode_open(node);
1033 if (node->sf_file == NULL) {
1034 mutex_exit(&sffs_lock);
1035 return (EINVAL);
1036 }
1037
1038 sfnode_invalidate_stat_cache(node);
1039
1040 if (ioflag & FAPPEND) {
1041 uint64_t endoffile;
1042
1043 error = sfprov_get_size(node->sf_sffs->sf_handle,
1044 node->sf_path, &endoffile);
1045 if (error == ENOENT)
1046 sfnode_make_stale(node);
1047 if (error != 0) {
1048 mutex_exit(&sffs_lock);
1049 return (error);
1050 }
1051 uiop->uio_loffset = endoffile;
1052 }
1053
1054 if (vp->v_type != VREG || uiop->uio_loffset < 0) {
1055 mutex_exit(&sffs_lock);
1056 return (EINVAL);
1057 }
1058 if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
1059 limit = MAXOFFSET_T;
1060
1061 if (uiop->uio_loffset >= limit) {
1062 proc_t *p = ttoproc(curthread);
1063 mutex_enter(&p->p_lock);
1064 (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
1065 p, RCA_UNSAFE_SIGINFO);
1066 mutex_exit(&p->p_lock);
1067 mutex_exit(&sffs_lock);
1068 return (EFBIG);
1069 }
1070
1071 if (uiop->uio_loffset >= MAXOFFSET_T) {
1072 mutex_exit(&sffs_lock);
1073 return (EFBIG);
1074 }
1075
1076 total = uiop->uio_resid;
1077 if (total == 0) {
1078 mutex_exit(&sffs_lock);
1079 return (0);
1080 }
1081
1082 do {
1083 offset = uiop->uio_offset;
1084 bytes = MIN(PAGESIZE, uiop->uio_resid);
1085 if (offset + bytes >= limit) {
1086 if (offset >= limit) {
1087 error = EFBIG;
1088 break;
1089 }
1090 bytes = limit - offset;
1091 }
1092 error = uiomove(sffs_buffer, bytes, UIO_WRITE, uiop);
1093 if (error != 0)
1094 break;
1095 done = bytes;
1096 if (error == 0)
1097 error = sfprov_write(node->sf_file, sffs_buffer,
1098 offset, &done);
1099 total -= done;
1100 if (done != bytes) {
1101 uiop->uio_resid += bytes - done;
1102 break;
1103 }
1104 } while (error == 0 && uiop->uio_resid > 0 && done > 0);
1105
1106 mutex_exit(&sffs_lock);
1107
1108 /*
1109 * A short write is never really an error.
1110 */
1111 if (total != uiop->uio_resid)
1112 error = 0;
1113 return (error);
1114}
1115
1116/*ARGSUSED*/
1117static int
1118sffs_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
1119{
1120 sfnode_t *node = VN2SFN(vp);
1121 int error;
1122
1123 mutex_enter(&sffs_lock);
1124 error = sfnode_access(node, mode, cr);
1125 mutex_exit(&sffs_lock);
1126 return (error);
1127}
1128
1129/*
1130 * Lookup an entry in a directory and create a new vnode if found.
1131 */
1132/* ARGSUSED3 */
1133static int
1134sffs_lookup(
1135 vnode_t *dvp, /* the directory vnode */
1136 char *name, /* the name of the file or directory */
1137 vnode_t **vpp, /* the vnode we found or NULL */
1138 struct pathname *pnp,
1139 int flags,
1140 vnode_t *rdir,
1141 cred_t *cred,
1142 caller_context_t *ct,
1143 int *direntflags,
1144 struct pathname *realpnp)
1145{
1146 int error;
1147 sfnode_t *node;
1148
1149 /*
1150 * dvp must be a directory
1151 */
1152 if (dvp->v_type != VDIR)
1153 return (ENOTDIR);
1154
1155 /*
1156 * An empty component name or just "." means the directory itself.
1157 * Don't do any further lookup or checking.
1158 */
1159 if (strcmp(name, "") == 0 || strcmp(name, ".") == 0) {
1160 VN_HOLD(dvp);
1161 *vpp = dvp;
1162 return (0);
1163 }
1164
1165 /*
1166 * Check permission to look at this directory. We always allow "..".
1167 */
1168 mutex_enter(&sffs_lock);
1169 if (strcmp(name, "..") != 0) {
1170 error = sfnode_access(VN2SFN(dvp), VEXEC, cred);
1171 if (error) {
1172 mutex_exit(&sffs_lock);
1173 return (error);
1174 }
1175 }
1176
1177 /*
1178 * Lookup the node.
1179 */
1180 node = sfnode_lookup(VN2SFN(dvp), name, VNON, NULL, 0, NULL);
1181 if (node != NULL)
1182 *vpp = sfnode_get_vnode(node);
1183 mutex_exit(&sffs_lock);
1184 return ((node == NULL) ? ENOENT : 0);
1185}
1186
1187/*ARGSUSED*/
1188static int
1189sffs_create(
1190 vnode_t *dvp,
1191 char *name,
1192 struct vattr *vap,
1193 vcexcl_t exclusive,
1194 int mode,
1195 vnode_t **vpp,
1196 cred_t *cr,
1197 int flag,
1198 caller_context_t *ct,
1199 vsecattr_t *vsecp)
1200{
1201 vnode_t *vp;
1202 sfnode_t *node;
1203 int error;
1204
1205 ASSERT(name != NULL);
1206
1207 /*
1208 * this is used for regular files, not mkdir
1209 */
1210 if (vap->va_type == VDIR)
1211 return (EISDIR);
1212 if (vap->va_type != VREG)
1213 return (EINVAL);
1214
1215 /*
1216 * is this a pre-existing file?
1217 */
1218 error = sffs_lookup(dvp, name, &vp,
1219 NULL, 0, NULL, cr, ct, NULL, NULL);
1220 if (error == ENOENT)
1221 vp = NULL;
1222 else if (error != 0)
1223 return (error);
1224
1225 /*
1226 * Operation on a pre-existing file.
1227 */
1228 if (vp != NULL) {
1229 if (exclusive == EXCL) {
1230 VN_RELE(vp);
1231 return (EEXIST);
1232 }
1233 if (vp->v_type == VDIR && (mode & VWRITE) == VWRITE) {
1234 VN_RELE(vp);
1235 return (EISDIR);
1236 }
1237
1238 mutex_enter(&sffs_lock);
1239 node = VN2SFN(vp);
1240 error = sfnode_access(node, mode, cr);
1241 if (error != 0) {
1242 mutex_exit(&sffs_lock);
1243 VN_RELE(vp);
1244 return (error);
1245 }
1246
1247 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1248
1249 /*
1250 * handle truncating an existing file
1251 */
1252 if (vp->v_type == VREG && (vap->va_mask & AT_SIZE) &&
1253 vap->va_size == 0) {
1254 sfnode_open(node);
1255 if (node->sf_path == NULL)
1256 error = ENOENT;
1257 else
1258 error = sfprov_trunc(node->sf_sffs->sf_handle,
1259 node->sf_path);
1260 if (error) {
1261 mutex_exit(&sffs_lock);
1262 VN_RELE(vp);
1263 return (error);
1264 }
1265 }
1266 mutex_exit(&sffs_lock);
1267 *vpp = vp;
1268 return (0);
1269 }
1270
1271 /*
1272 * Create a new node. First check for a race creating it.
1273 */
1274 mutex_enter(&sffs_lock);
1275 node = sfnode_lookup(VN2SFN(dvp), name, VNON, NULL, 0, NULL);
1276 if (node != NULL) {
1277 mutex_exit(&sffs_lock);
1278 return (EEXIST);
1279 }
1280
1281 /*
1282 * Doesn't exist yet and we have the lock, so create it.
1283 */
1284 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1285 int lookuperr;
1286 node = sfnode_lookup(VN2SFN(dvp), name, VREG, NULL, 0, &lookuperr);
1287 if (node && (vap->va_mask & AT_MODE)) {
1288 timestruc_t dummy;
1289 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
1290 AT_MODE, vap->va_mode, dummy, dummy, dummy);
1291 if (error)
1292 cmn_err(CE_WARN, "sffs_create: set_mode(%s, %o) failed"
1293 " rc=%d", node->sf_path, vap->va_mode, error);
1294 }
1295
1296 if (node && node->sf_parent)
1297 sfnode_clear_dir_list(node->sf_parent);
1298
1299 mutex_exit(&sffs_lock);
1300 if (node == NULL)
1301 return (lookuperr);
1302 *vpp = sfnode_get_vnode(node);
1303 return (0);
1304}
1305
1306/*ARGSUSED*/
1307static int
1308sffs_mkdir(
1309 vnode_t *dvp,
1310 char *nm,
1311 vattr_t *va,
1312 vnode_t **vpp,
1313 cred_t *cred,
1314 caller_context_t *ct,
1315 int flags,
1316 vsecattr_t *vsecp)
1317{
1318 sfnode_t *node;
1319 vnode_t *vp;
1320 int error;
1321
1322 /*
1323 * These should never happen
1324 */
1325 ASSERT(nm != NULL);
1326 ASSERT(strcmp(nm, "") != 0);
1327 ASSERT(strcmp(nm, ".") != 0);
1328 ASSERT(strcmp(nm, "..") != 0);
1329
1330 /*
1331 * Do an unlocked look up first
1332 */
1333 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1334 if (error == 0) {
1335 VN_RELE(vp);
1336 return (EEXIST);
1337 }
1338 if (error != ENOENT)
1339 return (error);
1340
1341 /*
1342 * Must be able to write in current directory
1343 */
1344 mutex_enter(&sffs_lock);
1345 error = sfnode_access(VN2SFN(dvp), VWRITE, cred);
1346 if (error) {
1347 mutex_exit(&sffs_lock);
1348 return (error);
1349 }
1350
1351 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1352 int lookuperr = EACCES;
1353 node = sfnode_lookup(VN2SFN(dvp), nm, VDIR, NULL, 0, &lookuperr);
1354 if (node && (va->va_mask & AT_MODE)) {
1355 timestruc_t dummy;
1356 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
1357 AT_MODE, va->va_mode, dummy, dummy, dummy);
1358 if (error)
1359 cmn_err(CE_WARN, "sffs_mkdir: set_mode(%s, %o) failed"
1360 " rc=%d", node->sf_path, va->va_mode, error);
1361 }
1362
1363 if (node && node->sf_parent)
1364 sfnode_clear_dir_list(node->sf_parent);
1365
1366 mutex_exit(&sffs_lock);
1367 if (node == NULL)
1368 return (lookuperr);
1369 *vpp = sfnode_get_vnode(node);
1370 return (0);
1371}
1372
1373/*ARGSUSED*/
1374static int
1375sffs_rmdir(
1376 struct vnode *dvp,
1377 char *nm,
1378 vnode_t *cdir,
1379 cred_t *cred,
1380 caller_context_t *ct,
1381 int flags)
1382{
1383 sfnode_t *node;
1384 vnode_t *vp;
1385 int error;
1386
1387 /*
1388 * Return error when removing . and ..
1389 */
1390 if (strcmp(nm, ".") == 0 || strcmp(nm, "") == 0)
1391 return (EINVAL);
1392 if (strcmp(nm, "..") == 0)
1393 return (EEXIST);
1394
1395 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1396 if (error)
1397 return (error);
1398 if (vp->v_type != VDIR) {
1399 VN_RELE(vp);
1400 return (ENOTDIR);
1401 }
1402
1403#if 0
1404 if (vn_vfswlock(vp)) {
1405 VN_RELE(vp);
1406 return (EBUSY);
1407 }
1408#endif
1409
1410 if (vn_mountedvfs(vp)) {
1411 VN_RELE(vp);
1412 return (EBUSY);
1413 }
1414
1415 node = VN2SFN(vp);
1416
1417 mutex_enter(&sffs_lock);
1418 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1419 if (error)
1420 goto done;
1421
1422 /*
1423 * If anything else is using this vnode, then fail the remove.
1424 * Why? Windows hosts can't remove something that is open,
1425 * so we have to sfprov_close() it first.
1426 * There is no errno for this - since it's not a problem on UNIX,
1427 * but EINVAL is the closest.
1428 */
1429 if (node->sf_file != NULL) {
1430 if (vp->v_count > 1) {
1431 error = EINVAL;
1432 goto done;
1433 }
1434 (void)sfprov_close(node->sf_file);
1435 node->sf_file = NULL;
1436 }
1437
1438 /*
1439 * Remove the directory on the host and mark the node as stale.
1440 */
1441 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1442 error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
1443 if (error == ENOENT || error == 0)
1444 sfnode_make_stale(node);
1445
1446 if (node->sf_parent)
1447 sfnode_clear_dir_list(node->sf_parent);
1448done:
1449 mutex_exit(&sffs_lock);
1450 VN_RELE(vp);
1451 return (error);
1452}
1453
1454
1455/*ARGSUSED*/
1456static int
1457sffs_remove(
1458 vnode_t *dvp,
1459 char *name,
1460 cred_t *cred,
1461 caller_context_t *ct,
1462 int flags)
1463{
1464 vnode_t *vp;
1465 sfnode_t *node;
1466 int error;
1467
1468 /*
1469 * These should never happen
1470 */
1471 ASSERT(name != NULL);
1472 ASSERT(strcmp(name, "..") != 0);
1473
1474 error = sffs_lookup(dvp, name, &vp,
1475 NULL, 0, NULL, cred, ct, NULL, NULL);
1476 if (error)
1477 return (error);
1478 node = VN2SFN(vp);
1479
1480 mutex_enter(&sffs_lock);
1481 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1482 if (error)
1483 goto done;
1484
1485 /*
1486 * If anything else is using this vnode, then fail the remove.
1487 * Why? Windows hosts can't sfprov_remove() a file that is open,
1488 * so we have to sfprov_close() it first.
1489 * There is no errno for this - since it's not a problem on UNIX,
1490 * but ETXTBSY is the closest.
1491 */
1492 if (node->sf_file != NULL) {
1493 if (vp->v_count > 1) {
1494 error = ETXTBSY;
1495 goto done;
1496 }
1497 (void)sfprov_close(node->sf_file);
1498 node->sf_file = NULL;
1499 }
1500
1501 /*
1502 * Remove the file on the host and mark the node as stale.
1503 */
1504 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1505
1506 error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
1507 if (error == ENOENT || error == 0)
1508 sfnode_make_stale(node);
1509
1510 if (node->sf_parent)
1511 sfnode_clear_dir_list(node->sf_parent);
1512done:
1513 mutex_exit(&sffs_lock);
1514 VN_RELE(vp);
1515 return (error);
1516}
1517
1518/*ARGSUSED*/
1519static int
1520sffs_rename(
1521 vnode_t *old_dir,
1522 char *old_nm,
1523 vnode_t *new_dir,
1524 char *new_nm,
1525 cred_t *cred,
1526 caller_context_t *ct,
1527 int flags)
1528{
1529 char *newpath;
1530 int error;
1531 sfnode_t *node;
1532
1533 if (strcmp(new_nm, "") == 0 ||
1534 strcmp(new_nm, ".") == 0 ||
1535 strcmp(new_nm, "..") == 0 ||
1536 strcmp(old_nm, "") == 0 ||
1537 strcmp(old_nm, ".") == 0 ||
1538 strcmp(old_nm, "..") == 0)
1539 return (EINVAL);
1540
1541 /*
1542 * make sure we have permission to do the rename
1543 */
1544 mutex_enter(&sffs_lock);
1545 error = sfnode_access(VN2SFN(old_dir), VEXEC | VWRITE, cred);
1546 if (error == 0 && new_dir != old_dir)
1547 error = sfnode_access(VN2SFN(new_dir), VEXEC | VWRITE, cred);
1548 if (error)
1549 goto done;
1550
1551 node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON, NULL, 0, NULL);
1552 if (node == NULL) {
1553 error = ENOENT;
1554 goto done;
1555 }
1556
1557 /*
1558 * Rename the file on the host and in our caches.
1559 */
1560 sfnode_invalidate_stat_cache(node);
1561 sfnode_invalidate_stat_cache(VN2SFN(old_dir));
1562 sfnode_invalidate_stat_cache(VN2SFN(new_dir));
1563
1564 newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
1565 error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
1566 node->sf_type == VDIR);
1567 if (error == 0)
1568 sfnode_rename(node, VN2SFN(new_dir), newpath);
1569 else {
1570 kmem_free(newpath, strlen(newpath) + 1);
1571 if (error == ENOENT)
1572 sfnode_make_stale(node);
1573 }
1574done:
1575 mutex_exit(&sffs_lock);
1576 return (error);
1577}
1578
1579
1580/*ARGSUSED*/
1581static int
1582sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
1583{
1584 sfnode_t *node;
1585 int error;
1586
1587 /*
1588 * Ask the host to sync any data it may have cached for open files.
1589 */
1590 mutex_enter(&sffs_lock);
1591 node = VN2SFN(vp);
1592 if (node->sf_file == NULL)
1593 error = EBADF;
1594 else if (node->sf_sffs->sf_fsync)
1595 error = sfprov_fsync(node->sf_file);
1596 else
1597 error = 0;
1598 mutex_exit(&sffs_lock);
1599 return (error);
1600}
1601
1602/*
1603 * This may be the last reference, possibly time to close the file and
1604 * destroy the vnode. If the sfnode is stale, we'll destroy that too.
1605 */
1606/*ARGSUSED*/
1607static void
1608#if defined(VBOX_VFS_SOLARIS_10U6)
1609sffs_inactive(vnode_t *vp, cred_t *cr)
1610#else
1611sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1612#endif
1613{
1614 sfnode_t *node;
1615
1616 /*
1617 * nothing to do if this isn't the last use
1618 */
1619 mutex_enter(&sffs_lock);
1620 node = VN2SFN(vp);
1621 mutex_enter(&vp->v_lock);
1622 if (vp->v_count > 1) {
1623 --vp->v_count;
1624 mutex_exit(&vp->v_lock);
1625 mutex_exit(&sffs_lock);
1626 return;
1627 }
1628
1629 /*
1630 * There should never be cached data, since we don't support mmap().
1631 */
1632 if (vn_has_cached_data(vp))
1633 panic("sffs_inactive() found cached data");
1634
1635 /*
1636 * destroy the vnode
1637 */
1638 node->sf_vnode = NULL;
1639 mutex_exit(&vp->v_lock);
1640 vn_invalid(vp);
1641 vn_free(vp);
1642 LogFlowFunc((" %s vnode cleared\n", node->sf_path));
1643
1644 /*
1645 * Close the sf_file for the node.
1646 */
1647 if (node->sf_file != NULL) {
1648 (void)sfprov_close(node->sf_file);
1649 node->sf_file = NULL;
1650 }
1651
1652 /*
1653 * Free the directory entries for the node. This should normally
1654 * have been taken care of in sffs_close(), but better safe than
1655 * sorry.
1656 */
1657 sfnode_clear_dir_list(node);
1658
1659 /*
1660 * If the node is stale, we can also destroy it.
1661 */
1662 if (node->sf_is_stale && node->sf_children == 0)
1663 sfnode_destroy(node);
1664
1665 mutex_exit(&sffs_lock);
1666 return;
1667}
1668
1669/*
1670 * All the work for this is really done in lookup.
1671 */
1672/*ARGSUSED*/
1673static int
1674sffs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
1675{
1676 return (0);
1677}
1678
1679/*
1680 * All the work for this is really done in inactive.
1681 */
1682/*ARGSUSED*/
1683static int
1684sffs_close(
1685 vnode_t *vp,
1686 int flag,
1687 int count,
1688 offset_t offset,
1689 cred_t *cr,
1690 caller_context_t *ct)
1691{
1692 sfnode_t *node;
1693
1694 mutex_enter(&sffs_lock);
1695 node = VN2SFN(vp);
1696
1697 /*
1698 * Free the directory entries for the node. We do this on this call
1699 * here because the directory node may not become inactive for a long
1700 * time after the readdir is over. Case in point, if somebody cd's into
1701 * the directory then it won't become inactive until they cd away again.
1702 * In such a case we would end up with the directory listing not getting
1703 * updated (i.e. the result of 'ls' always being the same) until they
1704 * change the working directory.
1705 */
1706 sfnode_clear_dir_list(node);
1707
1708 sfnode_invalidate_stat_cache(node);
1709
1710 mutex_exit(&sffs_lock);
1711 return (0);
1712}
1713
1714/* ARGSUSED */
1715static int
1716sffs_seek(vnode_t *v, offset_t o, offset_t *no, caller_context_t *ct)
1717{
1718 if (*no < 0 || *no > MAXOFFSET_T)
1719 return (EINVAL);
1720 return (0);
1721}
1722
1723
1724
1725/*
1726 * By returning an error for this, we prevent anything in sffs from
1727 * being re-exported by NFS
1728 */
1729/* ARGSUSED */
1730static int
1731sffs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
1732{
1733 return (ENOTSUP);
1734}
1735
1736/*
1737 * vnode operations for regular files
1738 */
1739const fs_operation_def_t sffs_ops_template[] = {
1740#if defined(VBOX_VFS_SOLARIS_10U6)
1741 VOPNAME_ACCESS, sffs_access,
1742 VOPNAME_CLOSE, sffs_close,
1743 VOPNAME_CREATE, sffs_create,
1744 VOPNAME_FID, sffs_fid,
1745 VOPNAME_FSYNC, sffs_fsync,
1746 VOPNAME_GETATTR, sffs_getattr,
1747 VOPNAME_INACTIVE, sffs_inactive,
1748 VOPNAME_LOOKUP, sffs_lookup,
1749 VOPNAME_MKDIR, sffs_mkdir,
1750 VOPNAME_OPEN, sffs_open,
1751 VOPNAME_PATHCONF, sffs_pathconf,
1752 VOPNAME_READ, sffs_read,
1753 VOPNAME_READDIR, sffs_readdir,
1754 VOPNAME_REMOVE, sffs_remove,
1755 VOPNAME_RENAME, sffs_rename,
1756 VOPNAME_RMDIR, sffs_rmdir,
1757 VOPNAME_SEEK, sffs_seek,
1758 VOPNAME_SETATTR, sffs_setattr,
1759 VOPNAME_SPACE, sffs_space,
1760 VOPNAME_WRITE, sffs_write,
1761 NULL, NULL
1762#else
1763 VOPNAME_ACCESS, { .vop_access = sffs_access },
1764 VOPNAME_CLOSE, { .vop_close = sffs_close },
1765 VOPNAME_CREATE, { .vop_create = sffs_create },
1766 VOPNAME_FID, { .vop_fid = sffs_fid },
1767 VOPNAME_FSYNC, { .vop_fsync = sffs_fsync },
1768 VOPNAME_GETATTR, { .vop_getattr = sffs_getattr },
1769 VOPNAME_INACTIVE, { .vop_inactive = sffs_inactive },
1770 VOPNAME_LOOKUP, { .vop_lookup = sffs_lookup },
1771 VOPNAME_MKDIR, { .vop_mkdir = sffs_mkdir },
1772 VOPNAME_OPEN, { .vop_open = sffs_open },
1773 VOPNAME_PATHCONF, { .vop_pathconf = sffs_pathconf },
1774 VOPNAME_READ, { .vop_read = sffs_read },
1775 VOPNAME_READDIR, { .vop_readdir = sffs_readdir },
1776 VOPNAME_REMOVE, { .vop_remove = sffs_remove },
1777 VOPNAME_RENAME, { .vop_rename = sffs_rename },
1778 VOPNAME_RMDIR, { .vop_rmdir = sffs_rmdir },
1779 VOPNAME_SEEK, { .vop_seek = sffs_seek },
1780 VOPNAME_SETATTR, { .vop_setattr = sffs_setattr },
1781 VOPNAME_SPACE, { .vop_space = sffs_space },
1782 VOPNAME_WRITE, { .vop_write = sffs_write },
1783 NULL, NULL
1784#endif
1785};
1786
1787/*
1788 * Also, init and fini functions...
1789 */
1790int
1791sffs_vnode_init(void)
1792{
1793 int err;
1794
1795 err = vn_make_ops("sffs", sffs_ops_template, &sffs_ops);
1796 if (err)
1797 return (err);
1798
1799 avl_create(&sfnodes, sfnode_compare, sizeof (sfnode_t),
1800 offsetof(sfnode_t, sf_linkage));
1801 avl_create(&stale_sfnodes, sfnode_compare, sizeof (sfnode_t),
1802 offsetof(sfnode_t, sf_linkage));
1803
1804 sffs_buffer = kmem_alloc(PAGESIZE, KM_SLEEP);
1805
1806 return (0);
1807}
1808
1809void
1810sffs_vnode_fini(void)
1811{
1812 if (sffs_ops)
1813 vn_freevnodeops(sffs_ops);
1814 ASSERT(avl_first(&sfnodes) == NULL);
1815 avl_destroy(&sfnodes);
1816 if (sffs_buffer != NULL) {
1817 kmem_free(sffs_buffer, PAGESIZE);
1818 sffs_buffer = NULL;
1819 }
1820}
1821
1822/*
1823 * Utility at unmount to get all nodes in that mounted filesystem removed.
1824 */
1825int
1826sffs_purge(struct sffs_data *sffs)
1827{
1828 sfnode_t *node;
1829 sfnode_t *prev;
1830
1831 /*
1832 * Check that no vnodes are active.
1833 */
1834 if (sffs->sf_rootnode->v_count > 1)
1835 return (-1);
1836 for (node = avl_first(&sfnodes); node;
1837 node = AVL_NEXT(&sfnodes, node)) {
1838 if (node->sf_sffs == sffs && node->sf_vnode &&
1839 node->sf_vnode != sffs->sf_rootnode)
1840 return (-1);
1841 }
1842 for (node = avl_first(&stale_sfnodes); node;
1843 node = AVL_NEXT(&stale_sfnodes, node)) {
1844 if (node->sf_sffs == sffs && node->sf_vnode &&
1845 node->sf_vnode != sffs->sf_rootnode)
1846 return (-1);
1847 }
1848
1849 /*
1850 * All clear to destroy all node information. Since there are no
1851 * vnodes, the make stale will cause deletion.
1852 */
1853 VN_RELE(sffs->sf_rootnode);
1854 mutex_enter(&sffs_lock);
1855 for (prev = NULL;;) {
1856 if (prev == NULL)
1857 node = avl_first(&sfnodes);
1858 else
1859 node = AVL_NEXT(&sfnodes, prev);
1860
1861 if (node == NULL)
1862 break;
1863
1864 if (node->sf_sffs == sffs) {
1865 if (node->sf_vnode != NULL)
1866 panic("vboxfs: purge hit active vnode");
1867 sfnode_make_stale(node);
1868 } else {
1869 prev = node;
1870 }
1871 }
1872 mutex_exit(&sffs_lock);
1873 return (0);
1874}
1875
1876static void
1877sfnode_print(sfnode_t *node)
1878{
1879 Log(("0x%p", node));
1880 Log((" type=%s (%d)",
1881 node->sf_type == VDIR ? "VDIR" :
1882 node->sf_type == VNON ? "VNON" :
1883 node->sf_type == VREG ? "VREG" : "other", node->sf_type));
1884 Log((" ino=%d", (uint_t)node->sf_ino));
1885 Log((" path=%s", node->sf_path));
1886 Log((" parent=0x%p", node->sf_parent));
1887 if (node->sf_children)
1888 Log((" children=%d", node->sf_children));
1889 if (node->sf_vnode)
1890 Log((" vnode=0x%p", node->sf_vnode));
1891 Log(("%s\n", node->sf_is_stale ? " STALE" : ""));
1892}
1893
1894void
1895sfnode_list()
1896{
1897 sfnode_t *n;
1898 for (n = avl_first(&sfnodes); n != NULL; n = AVL_NEXT(&sfnodes, n))
1899 sfnode_print(n);
1900 for (n = avl_first(&stale_sfnodes); n != NULL;
1901 n = AVL_NEXT(&stale_sfnodes, n))
1902 sfnode_print(n);
1903}
1904
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