VirtualBox

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

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

Additions/solaris/SharedFolders: Pass file/dir mode directly to create/mkdir calls instead of using set_attr explicitly after each call.

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