VirtualBox

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

Last change on this file since 30689 was 30527, checked in by vboxsync, 15 years ago

Additions/Solaris/SharedFolders: optionally honor fsync requests from the guest.

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