VirtualBox

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

Last change on this file since 39260 was 39260, checked in by vboxsync, 13 years ago

Additions/solaris/SharedFolders: fix recursive mutex.

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