VirtualBox

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

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

Additions/solaris/SharedFolders: allow COW mappings and disallow MAP_SHARED with PROT_WRITE mmaps that writes back to the file. Fixes public #9917.

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