VirtualBox

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

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

Additions/Solaris/SharedFolders: fix a possible panic when file creation fails + logging on failures.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 42.2 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 && node->sf_parent)
1275 sfnode_clear_dir_list(node->sf_parent);
1276
1277 mutex_exit(&sffs_lock);
1278 if (node == NULL)
1279 {
1280 cmn_err(CE_NOTE, "sffs_create: sfnode_lookup() failed.\n");
1281 return (EINVAL);
1282 }
1283 *vpp = sfnode_get_vnode(node);
1284 return (0);
1285}
1286
1287/*ARGSUSED*/
1288static int
1289sffs_mkdir(
1290 vnode_t *dvp,
1291 char *nm,
1292 vattr_t *va,
1293 vnode_t **vpp,
1294 cred_t *cred,
1295 caller_context_t *ct,
1296 int flags,
1297 vsecattr_t *vsecp)
1298{
1299 sfnode_t *node;
1300 vnode_t *vp;
1301 int error;
1302
1303 /*
1304 * These should never happen
1305 */
1306 ASSERT(nm != NULL);
1307 ASSERT(strcmp(nm, "") != 0);
1308 ASSERT(strcmp(nm, ".") != 0);
1309 ASSERT(strcmp(nm, "..") != 0);
1310
1311 /*
1312 * Do an unlocked look up first
1313 */
1314 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1315 if (error == 0) {
1316 VN_RELE(vp);
1317 return (EEXIST);
1318 }
1319 if (error != ENOENT)
1320 return (error);
1321
1322 /*
1323 * Must be able to write in current directory
1324 */
1325 mutex_enter(&sffs_lock);
1326 error = sfnode_access(VN2SFN(dvp), VWRITE, cred);
1327 if (error) {
1328 mutex_exit(&sffs_lock);
1329 return (error);
1330 }
1331
1332 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1333
1334 node = sfnode_lookup(VN2SFN(dvp), nm, VDIR, NULL, 0);
1335 if (node && (va->va_mask & AT_MODE)) {
1336 timestruc_t dummy;
1337 error = sfprov_set_attr(node->sf_sffs->sf_handle, node->sf_path,
1338 AT_MODE, va->va_mode, dummy, dummy, dummy);
1339 if (error)
1340 cmn_err(CE_WARN, "sffs_mkdir: set_mode(%s, %o) failed"
1341 " rc=%d", node->sf_path, va->va_mode, error);
1342 }
1343
1344 if (node->sf_parent)
1345 sfnode_clear_dir_list(node->sf_parent);
1346
1347 mutex_exit(&sffs_lock);
1348 if (node == NULL)
1349 return (EACCES);
1350 *vpp = sfnode_get_vnode(node);
1351 return (0);
1352}
1353
1354/*ARGSUSED*/
1355static int
1356sffs_rmdir(
1357 struct vnode *dvp,
1358 char *nm,
1359 vnode_t *cdir,
1360 cred_t *cred,
1361 caller_context_t *ct,
1362 int flags)
1363{
1364 sfnode_t *node;
1365 vnode_t *vp;
1366 int error;
1367
1368 /*
1369 * Return error when removing . and ..
1370 */
1371 if (strcmp(nm, ".") == 0 || strcmp(nm, "") == 0)
1372 return (EINVAL);
1373 if (strcmp(nm, "..") == 0)
1374 return (EEXIST);
1375
1376 error = sffs_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
1377 if (error)
1378 return (error);
1379 if (vp->v_type != VDIR) {
1380 VN_RELE(vp);
1381 return (ENOTDIR);
1382 }
1383
1384#if 0
1385 if (vn_vfswlock(vp)) {
1386 VN_RELE(vp);
1387 return (EBUSY);
1388 }
1389#endif
1390
1391 if (vn_mountedvfs(vp)) {
1392 VN_RELE(vp);
1393 return (EBUSY);
1394 }
1395
1396 node = VN2SFN(vp);
1397
1398 mutex_enter(&sffs_lock);
1399 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1400 if (error)
1401 goto done;
1402
1403 /*
1404 * If anything else is using this vnode, then fail the remove.
1405 * Why? Windows hosts can't remove something that is open,
1406 * so we have to sfprov_close() it first.
1407 * There is no errno for this - since it's not a problem on UNIX,
1408 * but EINVAL is the closest.
1409 */
1410 if (node->sf_file != NULL) {
1411 if (vp->v_count > 1) {
1412 error = EINVAL;
1413 goto done;
1414 }
1415 (void)sfprov_close(node->sf_file);
1416 node->sf_file = NULL;
1417 }
1418
1419 /*
1420 * Remove the directory on the host and mark the node as stale.
1421 */
1422 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1423 error = sfprov_rmdir(node->sf_sffs->sf_handle, node->sf_path);
1424 if (error == ENOENT || error == 0)
1425 sfnode_make_stale(node);
1426
1427 if (node->sf_parent)
1428 sfnode_clear_dir_list(node->sf_parent);
1429done:
1430 mutex_exit(&sffs_lock);
1431 VN_RELE(vp);
1432 return (error);
1433}
1434
1435
1436/*ARGSUSED*/
1437static int
1438sffs_remove(
1439 vnode_t *dvp,
1440 char *name,
1441 cred_t *cred,
1442 caller_context_t *ct,
1443 int flags)
1444{
1445 vnode_t *vp;
1446 sfnode_t *node;
1447 int error;
1448
1449 /*
1450 * These should never happen
1451 */
1452 ASSERT(name != NULL);
1453 ASSERT(strcmp(name, "..") != 0);
1454
1455 error = sffs_lookup(dvp, name, &vp,
1456 NULL, 0, NULL, cred, ct, NULL, NULL);
1457 if (error)
1458 return (error);
1459 node = VN2SFN(vp);
1460
1461 mutex_enter(&sffs_lock);
1462 error = sfnode_access(VN2SFN(dvp), VEXEC | VWRITE, cred);
1463 if (error)
1464 goto done;
1465
1466 /*
1467 * If anything else is using this vnode, then fail the remove.
1468 * Why? Windows hosts can't sfprov_remove() a file that is open,
1469 * so we have to sfprov_close() it first.
1470 * There is no errno for this - since it's not a problem on UNIX,
1471 * but ETXTBSY is the closest.
1472 */
1473 if (node->sf_file != NULL) {
1474 if (vp->v_count > 1) {
1475 error = ETXTBSY;
1476 goto done;
1477 }
1478 (void)sfprov_close(node->sf_file);
1479 node->sf_file = NULL;
1480 }
1481
1482 /*
1483 * Remove the file on the host and mark the node as stale.
1484 */
1485 sfnode_invalidate_stat_cache(VN2SFN(dvp));
1486
1487 error = sfprov_remove(node->sf_sffs->sf_handle, node->sf_path);
1488 if (error == ENOENT || error == 0)
1489 sfnode_make_stale(node);
1490
1491 if (node->sf_parent)
1492 sfnode_clear_dir_list(node->sf_parent);
1493done:
1494 mutex_exit(&sffs_lock);
1495 VN_RELE(vp);
1496 return (error);
1497}
1498
1499/*ARGSUSED*/
1500static int
1501sffs_rename(
1502 vnode_t *old_dir,
1503 char *old_nm,
1504 vnode_t *new_dir,
1505 char *new_nm,
1506 cred_t *cred,
1507 caller_context_t *ct,
1508 int flags)
1509{
1510 char *newpath;
1511 int error;
1512 sfnode_t *node;
1513
1514 if (strcmp(new_nm, "") == 0 ||
1515 strcmp(new_nm, ".") == 0 ||
1516 strcmp(new_nm, "..") == 0 ||
1517 strcmp(old_nm, "") == 0 ||
1518 strcmp(old_nm, ".") == 0 ||
1519 strcmp(old_nm, "..") == 0)
1520 return (EINVAL);
1521
1522 /*
1523 * make sure we have permission to do the rename
1524 */
1525 mutex_enter(&sffs_lock);
1526 error = sfnode_access(VN2SFN(old_dir), VEXEC | VWRITE, cred);
1527 if (error == 0 && new_dir != old_dir)
1528 error = sfnode_access(VN2SFN(new_dir), VEXEC | VWRITE, cred);
1529 if (error)
1530 goto done;
1531
1532 node = sfnode_lookup(VN2SFN(old_dir), old_nm, VNON, NULL, 0);
1533 if (node == NULL) {
1534 error = ENOENT;
1535 goto done;
1536 }
1537
1538 /*
1539 * Rename the file on the host and in our caches.
1540 */
1541 sfnode_invalidate_stat_cache(node);
1542 sfnode_invalidate_stat_cache(VN2SFN(old_dir));
1543 sfnode_invalidate_stat_cache(VN2SFN(new_dir));
1544
1545 newpath = sfnode_construct_path(VN2SFN(new_dir), new_nm);
1546 error = sfprov_rename(node->sf_sffs->sf_handle, node->sf_path, newpath,
1547 node->sf_type == VDIR);
1548 if (error == 0)
1549 sfnode_rename(node, VN2SFN(new_dir), newpath);
1550 else {
1551 kmem_free(newpath, strlen(newpath) + 1);
1552 if (error == ENOENT)
1553 sfnode_make_stale(node);
1554 }
1555done:
1556 mutex_exit(&sffs_lock);
1557 return (error);
1558}
1559
1560
1561/*ARGSUSED*/
1562static int
1563sffs_fsync(vnode_t *vp, int flag, cred_t *cr, caller_context_t *ct)
1564{
1565 sfnode_t *node;
1566 int error;
1567
1568 /*
1569 * Ask the host to sync any data it may have cached for open files.
1570 */
1571 mutex_enter(&sffs_lock);
1572 node = VN2SFN(vp);
1573 if (node->sf_file == NULL)
1574 error = EBADF;
1575 else if (node->sf_sffs->sf_fsync)
1576 error = sfprov_fsync(node->sf_file);
1577 else
1578 error = 0;
1579 mutex_exit(&sffs_lock);
1580 return (error);
1581}
1582
1583/*
1584 * This may be the last reference, possibly time to close the file and
1585 * destroy the vnode. If the sfnode is stale, we'll destroy that too.
1586 */
1587/*ARGSUSED*/
1588static void
1589#if defined(VBOX_VFS_SOLARIS_10U6)
1590sffs_inactive(vnode_t *vp, cred_t *cr)
1591#else
1592sffs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1593#endif
1594{
1595 sfnode_t *node;
1596
1597 /*
1598 * nothing to do if this isn't the last use
1599 */
1600 mutex_enter(&sffs_lock);
1601 node = VN2SFN(vp);
1602 mutex_enter(&vp->v_lock);
1603 if (vp->v_count > 1) {
1604 --vp->v_count;
1605 mutex_exit(&vp->v_lock);
1606 mutex_exit(&sffs_lock);
1607 return;
1608 }
1609
1610 /*
1611 * There should never be cached data, since we don't support mmap().
1612 */
1613 if (vn_has_cached_data(vp))
1614 panic("sffs_inactive() found cached data");
1615
1616 /*
1617 * destroy the vnode
1618 */
1619 node->sf_vnode = NULL;
1620 mutex_exit(&vp->v_lock);
1621 vn_invalid(vp);
1622 vn_free(vp);
1623 LogFlowFunc((" %s vnode cleared\n", node->sf_path));
1624
1625 /*
1626 * Close the sf_file for the node.
1627 */
1628 if (node->sf_file != NULL) {
1629 (void)sfprov_close(node->sf_file);
1630 node->sf_file = NULL;
1631 }
1632
1633 /*
1634 * Free the directory entries for the node. This should normally
1635 * have been taken care of in sffs_close(), but better safe than
1636 * sorry.
1637 */
1638 sfnode_clear_dir_list(node);
1639
1640 /*
1641 * If the node is stale, we can also destroy it.
1642 */
1643 if (node->sf_is_stale && node->sf_children == 0)
1644 sfnode_destroy(node);
1645
1646 mutex_exit(&sffs_lock);
1647 return;
1648}
1649
1650/*
1651 * All the work for this is really done in lookup.
1652 */
1653/*ARGSUSED*/
1654static int
1655sffs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
1656{
1657 return (0);
1658}
1659
1660/*
1661 * All the work for this is really done in inactive.
1662 */
1663/*ARGSUSED*/
1664static int
1665sffs_close(
1666 vnode_t *vp,
1667 int flag,
1668 int count,
1669 offset_t offset,
1670 cred_t *cr,
1671 caller_context_t *ct)
1672{
1673 sfnode_t *node;
1674
1675 mutex_enter(&sffs_lock);
1676 node = VN2SFN(vp);
1677
1678 /*
1679 * Free the directory entries for the node. We do this on this call
1680 * here because the directory node may not become inactive for a long
1681 * time after the readdir is over. Case in point, if somebody cd's into
1682 * the directory then it won't become inactive until they cd away again.
1683 * In such a case we would end up with the directory listing not getting
1684 * updated (i.e. the result of 'ls' always being the same) until they
1685 * change the working directory.
1686 */
1687 sfnode_clear_dir_list(node);
1688
1689 sfnode_invalidate_stat_cache(node);
1690
1691 mutex_exit(&sffs_lock);
1692 return (0);
1693}
1694
1695/* ARGSUSED */
1696static int
1697sffs_seek(vnode_t *v, offset_t o, offset_t *no, caller_context_t *ct)
1698{
1699 if (*no < 0 || *no > MAXOFFSET_T)
1700 return (EINVAL);
1701 return (0);
1702}
1703
1704
1705
1706/*
1707 * By returning an error for this, we prevent anything in sffs from
1708 * being re-exported by NFS
1709 */
1710/* ARGSUSED */
1711static int
1712sffs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
1713{
1714 return (ENOTSUP);
1715}
1716
1717/*
1718 * vnode operations for regular files
1719 */
1720const fs_operation_def_t sffs_ops_template[] = {
1721#if defined(VBOX_VFS_SOLARIS_10U6)
1722 VOPNAME_ACCESS, sffs_access,
1723 VOPNAME_CLOSE, sffs_close,
1724 VOPNAME_CREATE, sffs_create,
1725 VOPNAME_FID, sffs_fid,
1726 VOPNAME_FSYNC, sffs_fsync,
1727 VOPNAME_GETATTR, sffs_getattr,
1728 VOPNAME_INACTIVE, sffs_inactive,
1729 VOPNAME_LOOKUP, sffs_lookup,
1730 VOPNAME_MKDIR, sffs_mkdir,
1731 VOPNAME_OPEN, sffs_open,
1732 VOPNAME_PATHCONF, sffs_pathconf,
1733 VOPNAME_READ, sffs_read,
1734 VOPNAME_READDIR, sffs_readdir,
1735 VOPNAME_REMOVE, sffs_remove,
1736 VOPNAME_RENAME, sffs_rename,
1737 VOPNAME_RMDIR, sffs_rmdir,
1738 VOPNAME_SEEK, sffs_seek,
1739 VOPNAME_SETATTR, sffs_setattr,
1740 VOPNAME_SPACE, sffs_space,
1741 VOPNAME_WRITE, sffs_write,
1742 NULL, NULL
1743#else
1744 VOPNAME_ACCESS, { .vop_access = sffs_access },
1745 VOPNAME_CLOSE, { .vop_close = sffs_close },
1746 VOPNAME_CREATE, { .vop_create = sffs_create },
1747 VOPNAME_FID, { .vop_fid = sffs_fid },
1748 VOPNAME_FSYNC, { .vop_fsync = sffs_fsync },
1749 VOPNAME_GETATTR, { .vop_getattr = sffs_getattr },
1750 VOPNAME_INACTIVE, { .vop_inactive = sffs_inactive },
1751 VOPNAME_LOOKUP, { .vop_lookup = sffs_lookup },
1752 VOPNAME_MKDIR, { .vop_mkdir = sffs_mkdir },
1753 VOPNAME_OPEN, { .vop_open = sffs_open },
1754 VOPNAME_PATHCONF, { .vop_pathconf = sffs_pathconf },
1755 VOPNAME_READ, { .vop_read = sffs_read },
1756 VOPNAME_READDIR, { .vop_readdir = sffs_readdir },
1757 VOPNAME_REMOVE, { .vop_remove = sffs_remove },
1758 VOPNAME_RENAME, { .vop_rename = sffs_rename },
1759 VOPNAME_RMDIR, { .vop_rmdir = sffs_rmdir },
1760 VOPNAME_SEEK, { .vop_seek = sffs_seek },
1761 VOPNAME_SETATTR, { .vop_setattr = sffs_setattr },
1762 VOPNAME_SPACE, { .vop_space = sffs_space },
1763 VOPNAME_WRITE, { .vop_write = sffs_write },
1764 NULL, NULL
1765#endif
1766};
1767
1768/*
1769 * Also, init and fini functions...
1770 */
1771int
1772sffs_vnode_init(void)
1773{
1774 int err;
1775
1776 err = vn_make_ops("sffs", sffs_ops_template, &sffs_ops);
1777 if (err)
1778 return (err);
1779
1780 avl_create(&sfnodes, sfnode_compare, sizeof (sfnode_t),
1781 offsetof(sfnode_t, sf_linkage));
1782 avl_create(&stale_sfnodes, sfnode_compare, sizeof (sfnode_t),
1783 offsetof(sfnode_t, sf_linkage));
1784
1785 sffs_buffer = kmem_alloc(PAGESIZE, KM_SLEEP);
1786
1787 return (0);
1788}
1789
1790void
1791sffs_vnode_fini(void)
1792{
1793 if (sffs_ops)
1794 vn_freevnodeops(sffs_ops);
1795 ASSERT(avl_first(&sfnodes) == NULL);
1796 avl_destroy(&sfnodes);
1797 if (sffs_buffer != NULL) {
1798 kmem_free(sffs_buffer, PAGESIZE);
1799 sffs_buffer = NULL;
1800 }
1801}
1802
1803/*
1804 * Utility at unmount to get all nodes in that mounted filesystem removed.
1805 */
1806int
1807sffs_purge(struct sffs_data *sffs)
1808{
1809 sfnode_t *node;
1810 sfnode_t *prev;
1811
1812 /*
1813 * Check that no vnodes are active.
1814 */
1815 if (sffs->sf_rootnode->v_count > 1)
1816 return (-1);
1817 for (node = avl_first(&sfnodes); node;
1818 node = AVL_NEXT(&sfnodes, node)) {
1819 if (node->sf_sffs == sffs && node->sf_vnode &&
1820 node->sf_vnode != sffs->sf_rootnode)
1821 return (-1);
1822 }
1823 for (node = avl_first(&stale_sfnodes); node;
1824 node = AVL_NEXT(&stale_sfnodes, node)) {
1825 if (node->sf_sffs == sffs && node->sf_vnode &&
1826 node->sf_vnode != sffs->sf_rootnode)
1827 return (-1);
1828 }
1829
1830 /*
1831 * All clear to destroy all node information. Since there are no
1832 * vnodes, the make stale will cause deletion.
1833 */
1834 VN_RELE(sffs->sf_rootnode);
1835 mutex_enter(&sffs_lock);
1836 for (prev = NULL;;) {
1837 if (prev == NULL)
1838 node = avl_first(&sfnodes);
1839 else
1840 node = AVL_NEXT(&sfnodes, prev);
1841
1842 if (node == NULL)
1843 break;
1844
1845 if (node->sf_sffs == sffs) {
1846 if (node->sf_vnode != NULL)
1847 panic("vboxfs: purge hit active vnode");
1848 sfnode_make_stale(node);
1849 } else {
1850 prev = node;
1851 }
1852 }
1853 mutex_exit(&sffs_lock);
1854 return (0);
1855}
1856
1857static void
1858sfnode_print(sfnode_t *node)
1859{
1860 Log(("0x%p", node));
1861 Log((" type=%s (%d)",
1862 node->sf_type == VDIR ? "VDIR" :
1863 node->sf_type == VNON ? "VNON" :
1864 node->sf_type == VREG ? "VREG" : "other", node->sf_type));
1865 Log((" ino=%d", (uint_t)node->sf_ino));
1866 Log((" path=%s", node->sf_path));
1867 Log((" parent=0x%p", node->sf_parent));
1868 if (node->sf_children)
1869 Log((" children=%d", node->sf_children));
1870 if (node->sf_vnode)
1871 Log((" vnode=0x%p", node->sf_vnode));
1872 Log(("%s\n", node->sf_is_stale ? " STALE" : ""));
1873}
1874
1875void
1876sfnode_list()
1877{
1878 sfnode_t *n;
1879 for (n = avl_first(&sfnodes); n != NULL; n = AVL_NEXT(&sfnodes, n))
1880 sfnode_print(n);
1881 for (n = avl_first(&stale_sfnodes); n != NULL;
1882 n = AVL_NEXT(&stale_sfnodes, n))
1883 sfnode_print(n);
1884}
1885
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