diff options
Diffstat (limited to 'unionfs/netfs.c')
-rw-r--r-- | unionfs/netfs.c | 1170 |
1 files changed, 0 insertions, 1170 deletions
diff --git a/unionfs/netfs.c b/unionfs/netfs.c deleted file mode 100644 index 89d1bf67..00000000 --- a/unionfs/netfs.c +++ /dev/null @@ -1,1170 +0,0 @@ -/* Hurd unionfs - Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. - Written by Moritz Schulte <moritz@duesseldorf.ccc.de>. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or * (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#define _GNU_SOURCE - -#include <hurd/netfs.h> -#include <error.h> -#include <argz.h> -#include <stddef.h> -#include <fcntl.h> -#include <assert.h> -#include <string.h> -#include <stdio.h> -#include <hurd/paths.h> -#include <sys/mman.h> - -#include "unionfs.h" -#include "ulfs.h" -#include "node.h" -#include "lib.h" -#include "ncache.h" -#include "options.h" - -/* Return an argz string describing the current options. Fill *ARGZ - with a pointer to newly malloced storage holding the list and *LEN - to the length of that storage. */ -error_t -netfs_append_args (char **argz, size_t *argz_len) -{ - error_t err = 0; - - ulfs_iterate - { - if (! err) - if (unionfs_flags & FLAG_UNIONFS_MODE_DEBUG) - err = argz_add (argz, argz_len, - OPT_LONG (OPT_LONG_DEBUG)); - if (! err) - if (ulfs->flags & FLAG_ULFS_WRITABLE) - err = argz_add (argz, argz_len, - OPT_LONG (OPT_LONG_WRITABLE)); - if (! err) - if (ulfs->priority) - { - char *buf = NULL; - if ((err = asprintf (&buf, "%s=%s", OPT_LONG (OPT_LONG_PRIORITY), - ulfs->priority)) != -1) - { - err = argz_add (argz, argz_len, buf); - free (buf); - } - } - - if (! err) - { - if (ulfs->path) - err = argz_add (argz, argz_len, ulfs->path); - else - err = argz_add (argz, argz_len, - OPT_LONG (OPT_LONG_UNDERLYING)); - } - } - - return err; -} - -#ifndef __USE_FILE_OFFSET64 -#define OFFSET_T __off_t /* Size in bytes. */ -#else -#define OFFSET_T __off64_t /* Size in bytes. */ -#endif - -static error_t -_get_node_size (struct node *dir, OFFSET_T *off) -{ - size_t size = 0; - error_t err; - int count = 0; - node_dirent_t *dirent_start, *dirent_current; - node_dirent_t *dirent_list = NULL; - int first_entry = 2; - - int bump_size (const char *name) - { - size_t new_size = size + DIRENT_LEN (strlen (name)); - - size = new_size; - count ++; - return 1; - } - - err = node_entries_get (dir, &dirent_list); - if (err) - return err; - - for (dirent_start = dirent_list, count = 2; - dirent_start && first_entry > count; - dirent_start = dirent_start->next, count++); - - count = 0; - - /* Make space for the `.' and `..' entries. */ - if (first_entry == 0) - bump_size ("."); - if (first_entry <= 1) - bump_size (".."); - - /* See how much space we need for the result. */ - for (dirent_current = dirent_start; - dirent_current; - dirent_current = dirent_current->next) - if (! bump_size (dirent_current->dirent->d_name)) - break; - - free (dirent_list); - - *off = size; - - return 0; -} - - -/* Make sure that NP->nn_stat is filled with current information. - CRED identifies the user responsible for the operation. */ -error_t -netfs_validate_stat (struct node *np, struct iouser *cred) -{ - error_t err = 0; - - if (np != netfs_root_node) - { - if (! (np->nn->flags & FLAG_NODE_ULFS_UPTODATE)) - err = node_update (np); - if (! err) - { - int done = 0; - - node_ulfs_iterate_unlocked (np) - if ((! done) && port_valid (node_ulfs->port)) - { - err = io_stat (node_ulfs->port, &np->nn_stat); - if (! err) - np->nn_translated = np->nn_stat.st_mode; - done = 1; - } - if (! done) - err = ENOENT; /* FIXME? */ - } - } - else - { - _get_node_size (np, &np->nn_stat.st_size); - } - - return err; -} - -/* This should attempt a chmod call for the user specified by CRED on - locked node NP, to change the owner to UID and the group to GID. */ -error_t -netfs_attempt_chown (struct iouser *cred, struct node *np, - uid_t uid, uid_t gid) -{ - return EOPNOTSUPP; -} - -/* This should attempt a chauthor call for the user specified by CRED - on locked node NP, thereby changing the author to AUTHOR. */ -error_t -netfs_attempt_chauthor (struct iouser *cred, struct node *np, - uid_t author) -{ - return EOPNOTSUPP; -} - -/* This should attempt a chmod call for the user specified by CRED on - locked node NODE, to change the mode to MODE. Unlike the normal - Unix and Hurd meaning of chmod, this function is also used to - attempt to change files into other types. If such a transition is - attempted which is impossible, then return EOPNOTSUPP. */ -error_t -netfs_attempt_chmod (struct iouser *cred, struct node *np, - mode_t mode) -{ - return EOPNOTSUPP; -} - -/* Attempt to turn locked node NP (user CRED) into a symlink with - target NAME. */ -error_t -netfs_attempt_mksymlink (struct iouser *cred, struct node *np, - char *name) -{ - return EOPNOTSUPP; -} - -/* Attempt to turn NODE (user CRED) into a device. TYPE is either - S_IFBLK or S_IFCHR. NP is locked. */ -error_t -netfs_attempt_mkdev (struct iouser *cred, struct node *np, - mode_t type, dev_t indexes) -{ - return EOPNOTSUPP; -} - -/* Attempt to set the passive translator record for FILE to ARGZ (of - length ARGZLEN) for user CRED. NP is locked. */ -error_t -netfs_set_translator (struct iouser *cred, struct node *np, - char *argz, size_t argzlen) -{ - return EOPNOTSUPP; -} - -/* For locked node NODE with S_IPTRANS set in its mode, look up the - name of its translator. Store the name into newly malloced - storage, and return it in *ARGZ; set *ARGZ_LEN to the total length. */ -error_t -netfs_get_translator (struct node *node, char **argz, - size_t *argz_len) -{ - return EOPNOTSUPP; -} - -/* This should attempt a chflags call for the user specified by CRED - on locked node NP, to change the flags to FLAGS. */ -error_t -netfs_attempt_chflags (struct iouser *cred, struct node *np, - int flags) -{ - return EOPNOTSUPP; -} - -/* This should attempt a utimes call for the user specified by CRED on - locked node NP, to change the atime to ATIME and the mtime to - MTIME. If ATIME or MTIME is null, then set to the current time. */ -error_t -netfs_attempt_utimes (struct iouser *cred, struct node *np, - struct timespec *atime, struct timespec *mtime) -{ - return 0; -} - -/* This should attempt to set the size of the locked file NP (for user - CRED) to SIZE bytes long. */ -error_t -netfs_attempt_set_size (struct iouser *cred, struct node *np, - off_t size) -{ - return EOPNOTSUPP; -} - -/* This should attempt to fetch filesystem status information for the - remote filesystem, for the user CRED. NP is locked. */ -error_t -netfs_attempt_statfs (struct iouser *cred, struct node *np, - struct statfs *st) -{ - return EOPNOTSUPP; -} - -/* This should sync the locked file NP completely to disk, for the - user CRED. If WAIT is set, return only after the sync is - completely finished. */ -error_t -netfs_attempt_sync (struct iouser *cred, struct node *np, - int wait) -{ - return EOPNOTSUPP; -} - -/* This should sync the entire remote filesystem. If WAIT is set, - return only after the sync is completely finished. */ -error_t -netfs_attempt_syncfs (struct iouser *cred, int wait) -{ - return 0; -} - -/* lookup */ - -/* We don't use this functions, but it has to be defined. */ -error_t -netfs_attempt_lookup (struct iouser *user, struct node *dir, - char *name, struct node **node) -{ - return EOPNOTSUPP; -} - -/* Delete NAME in DIR (which is locked) for USER. */ -error_t -netfs_attempt_unlink (struct iouser *user, struct node *dir, - char *name) -{ - error_t err = 0; - mach_port_t p; - struct stat statbuf; - - node_update (dir); - - err = node_lookup_file (dir, name, 0, &p, &statbuf); - if (err) - return err; - - port_dealloc (p); - - err = fshelp_checkdirmod (&dir->nn_stat, &statbuf, user); - if (err) - return err; - - err = node_unlink_file (dir, name); - - return err; -} - -/* Attempt to rename the directory FROMDIR to TODIR. Note that neither - of the specific nodes are locked. */ -error_t -netfs_attempt_rename (struct iouser *user, struct node *fromdir, - char *fromname, struct node *todir, - char *toname, int excl) -{ - return EOPNOTSUPP; -} - -/* Attempt to create a new directory named NAME in DIR (which is - locked) for USER with mode MODE. */ -error_t -netfs_attempt_mkdir (struct iouser *user, struct node *dir, - char *name, mode_t mode) -{ - error_t err = 0; - mach_port_t p; - struct stat statbuf; - - node_update (dir); - - err = fshelp_checkdirmod (&dir->nn_stat, 0, user); - if (err) - goto exit; - - /* Special case for no UID processes (like login shell). */ - if ((!user->uids->ids) || (!user->uids->ids)) - { - err = EACCES; - goto exit; - } - - err = node_dir_create (dir, name, mode); - if (err) - goto exit; - - err = node_lookup_file (dir, name, 0, &p, &statbuf); - if (err) - { - node_dir_remove (dir, name); - goto exit; - } - - err = file_chown (p, user->uids->ids[0], user->gids->ids[0]); - if (err) - { - port_dealloc (p); - node_dir_remove (dir, name); - goto exit; - } - - port_dealloc (p); - - exit: - - return err; -} - -/* Attempt to remove directory named NAME in DIR (which is locked) for - USER. */ -error_t -netfs_attempt_rmdir (struct iouser *user, - struct node *dir, char *name) -{ - error_t err = 0; - mach_port_t p; - struct stat statbuf; - - node_update (dir); - - err = node_lookup_file (dir, name, 0, &p, &statbuf); - if (err) - return err; - - port_dealloc (p); - - err = fshelp_checkdirmod (&dir->nn_stat, &statbuf, user); - if (err) - return err; - - err = node_dir_remove (dir, name); - - return err; -} - -/* Create a link in DIR with name NAME to FILE for USER. Note that - neither DIR nor FILE are locked. If EXCL is set, do not delete the - target. Return EEXIST if NAME is already found in DIR. */ -error_t -netfs_attempt_link (struct iouser *user, struct node *dir, - struct node *file, char *name, int excl) -{ - return EOPNOTSUPP; -} - -/* Attempt to create an anonymous file related to DIR (which is - locked) for USER with MODE. Set *NP to the returned file upon - success. No matter what, unlock DIR. */ -error_t -netfs_attempt_mkfile (struct iouser *user, struct node *dir, - mode_t mode, struct node **np) -{ - mutex_unlock (&dir->lock); - return EOPNOTSUPP; -} - -/* (We don't use this function!) Attempt to create a file named NAME - in DIR (which is locked) for USER with MODE. Set *NP to the new - node upon return. On any error, clear *NP. *NP should be locked - on success; no matter what, unlock DIR before returning. */ -error_t -netfs_attempt_create_file (struct iouser *user, struct node *dir, - char *name, mode_t mode, struct node **np) -{ - mutex_unlock (&dir->lock); - return EOPNOTSUPP; -} - -/* We use this local interface to attempt_create file since we are - using our own netfs_S_dir_lookup. */ -error_t -netfs_attempt_create_file_reduced (struct iouser *user, struct node *dir, - char *name, mode_t mode, int flags) -{ - mach_port_t p; - error_t err; - struct stat statbuf; - - node_update (dir); - - err = fshelp_checkdirmod (&dir->nn_stat, 0, user); - if (err) - goto exit; - - /* Special case for no UID processes (like login shell). */ - if ((!user->uids->ids) || (!user->uids->ids)) - { - err = EACCES; - goto exit; - } - - mutex_unlock (&dir->lock); - err = node_lookup_file (dir, name, flags | O_CREAT, - &p, &statbuf); - mutex_lock (&dir->lock); - - if (err) - goto exit; - - err = file_chmod (p, mode); - if (err) - { - port_dealloc (p); - node_unlink_file (dir, name); - goto exit; - } - - err = file_chown (p, user->uids->ids[0], user->gids->ids[0]); - if (err) - { - port_dealloc (p); - node_unlink_file (dir, name); - goto exit; - } - - err = io_stat (p, &statbuf); - - /* Check file permissions. */ - if (! err && (flags & O_READ)) - err = fshelp_access (&statbuf, S_IREAD, user); - if (! err && (flags & O_WRITE)) - err = fshelp_access (&statbuf, S_IWRITE, user); - if (! err && (flags & O_EXEC)) - err = fshelp_access (&statbuf, S_IEXEC, user); - - if (err) - { - port_dealloc (p); - node_unlink_file (dir, name); - goto exit; - } - - port_dealloc (p); - - exit: - mutex_unlock (&dir->lock); - return err; -} - -/* Read the contents of locked node NP (a symlink), for USER, into - BUF. */ -error_t -netfs_attempt_readlink (struct iouser *user, struct node *np, - char *buf) -{ - return EOPNOTSUPP; -} - -/* libnetfs uses this functions once. */ -error_t -netfs_check_open_permissions (struct iouser *user, struct node *np, - int flags, int newnode) -{ - error_t err = 0; - - if (! err && (flags & O_READ)) - err = fshelp_access (&np->nn_stat, S_IREAD, user); - if (! err && (flags & O_WRITE)) - err = fshelp_access (&np->nn_stat, S_IWRITE, user); - if (! err && (flags & O_EXEC)) - err = fshelp_access (&np->nn_stat, S_IEXEC, user); - - return err; -} - -/* Read from the locked file NP for user CRED starting at OFFSET and - continuing for up to *LEN bytes. Put the data at DATA. Set *LEN - to the amount successfully read upon return. */ -error_t -netfs_attempt_read (struct iouser *cred, struct node *np, - off_t offset, size_t *len, void *data) -{ - *len = 0; - return 0; -} - -/* Write to the locked file NP for user CRED starting at OFSET and - continuing for up to *LEN bytes from DATA. Set *LEN to the amount - successfully written upon return. */ -error_t -netfs_attempt_write (struct iouser *cred, struct node *np, - off_t offset, size_t *len, void *data) -{ - /* Since unionfs only manages directories... */ - return EISDIR; -} - -/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and - O_EXEC) in *TYPES for locked file NP and user CRED. */ -error_t -netfs_report_access (struct iouser *cred, struct node *np, - int *types) -{ - *types = 0; - if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) - *types |= O_READ; - if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) - *types |= O_WRITE; - if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) - *types |= O_EXEC; - return 0; -} - -/* Create a new user from the specified UID and GID arrays. */ -struct iouser * -netfs_make_user (uid_t *uids, int nuids, uid_t *gids, int ngids) -{ - return NULL; -} - -/* Node NP has no more references; free all its associated storage. */ -void -netfs_node_norefs (struct node *np) -{ - node_destroy (np); -} - -error_t -netfs_attempt_lookup_improved (struct iouser *user, struct node *dir, - char *name, struct node **np, - int flags, int lastcomp, - mach_port_t *port, - mach_msg_type_name_t *port_type) -{ - mach_port_t p; - error_t err; - - mutex_lock (&dir->nn->lnode->lock); - - err = fshelp_access (&dir->nn_stat, S_IEXEC, user); - if (err) - goto exit; - - - if (! *name || ! strcmp (name, ".")) - { - - /* The same node is wanted. */ - *np = dir; - netfs_nref (*np); - - } - else if (! strcmp (name, "..")) - { - - /* We have to get the according light node first. */ - lnode_t *lnode = dir->nn->lnode; - node_t *node; - - err = ncache_node_lookup (lnode->dir, &node); - if (err) - goto exit; - - *np = node; - - } - else - { - - lnode_t *dir_lnode = dir->nn->lnode; - struct stat statbuf; - lnode_t *lnode = NULL; - - /* Lookup the node by it's name on the underlying - filesystems. */ - - err = node_update (dir); - - /* We have to unlock this node while doing lookups. */ - mutex_unlock (&dir_lnode->lock); - mutex_unlock (&dir->lock); - - err = node_lookup_file (dir, name, flags & ~(O_NOLINK|O_CREAT), - &p, &statbuf); - - mutex_lock (&dir->lock); - mutex_lock (&dir_lnode->lock); - - - if (err) - goto exit; - - if (S_ISDIR (statbuf.st_mode)) - { - node_t *node; - - /* We don't need this port directly. */ - port_dealloc (p); - - /* The found node is a directory, so we have to manage the - node. First we need the light node. */ - - err = lnode_get (dir_lnode, name, &lnode); - if (err == ENOENT) - { - /* It does not exist, we have to create it. */ - err = lnode_create (name, &lnode); - if (err) - goto exit; - - lnode_install (dir_lnode, lnode); - } - - /* Now we have a light node. */ - err = ncache_node_lookup (lnode, &node); - - /* This unlocks the node for us. */ - lnode_ref_remove (lnode); - - if (err) - goto exit; - - /* Got the node. */ - *np = node; - - } - else - { - /* The found node is not a directory. */ - mach_port_t p_restricted; - - if (! lastcomp) - { - /* We have not reached the last path component yet. */ - port_dealloc (p); - err = ENOTDIR; - goto exit; - } - - /* Check file permissions. */ - if (! err && (flags & O_READ)) - err = fshelp_access (&statbuf, S_IREAD, user); - if (! err && (flags & O_WRITE)) - err = fshelp_access (&statbuf, S_IWRITE, user); - if (! err && (flags & O_EXEC)) - err = fshelp_access (&statbuf, S_IEXEC, user); - - if (err) - { - port_dealloc (p); - goto exit; - } - - - /* A file node is successfully looked up. */ - err = io_restrict_auth (p, &p_restricted, - user->uids->ids, user->uids->num, - user->gids->ids, user->gids->num); - port_dealloc (p); - - if (err) - goto exit; - - /* Successfully restricted. */ - *port = p_restricted; - *port_type = MACH_MSG_TYPE_MOVE_SEND; - } - } - - exit: - - if (err) - *np = NULL; - else if (*np) - { - mutex_unlock (&(*np)->lock); - ncache_node_add (*np); - } - - mutex_unlock (&dir->nn->lnode->lock); - mutex_unlock (&dir->lock); - return err; -} - -/* We need our own, special implementation of netfs_S_dir_lookup, - because libnetfs does not (yet?) know about cases, in which the - servers wants to return (foreign) ports directly to the user, - instead of usual node structures. */ - -#define OPENONLY_STATE_MODES (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK) - -fshelp_fetch_root_callback1_t _netfs_translator_callback1; -fshelp_fetch_root_callback2_t _netfs_translator_callback2; - -error_t -netfs_S_dir_lookup (struct protid *diruser, - char *filename, - int flags, - mode_t mode, - retry_type *do_retry, - char *retry_name, - mach_port_t *retry_port, - mach_msg_type_name_t *retry_port_type) -{ - int create; /* true if O_CREAT flag set */ - int excl; /* true if O_EXCL flag set */ - int mustbedir = 0; /* true if the result must be S_IFDIR */ - int lastcomp = 0; /* true if we are at the last component */ - int newnode = 0; /* true if this node is newly created */ - int nsymlinks = 0; - struct node *dnp, *np; - char *nextname; - error_t error = 0; - struct protid *newpi; - struct iouser *user; - - if (!diruser) - return EOPNOTSUPP; - - create = (flags & O_CREAT); - excl = (flags & O_EXCL); - - /* Skip leading slashes */ - while (*filename == '/') - filename++; - - *retry_port_type = MACH_MSG_TYPE_MAKE_SEND; - *do_retry = FS_RETRY_NORMAL; - *retry_name = '\0'; - - if (*filename == '\0') - { - /* Set things up in the state expected by the code from gotit: on. */ - dnp = 0; - np = diruser->po->np; - mutex_lock (&np->lock); - netfs_nref (np); - goto gotit; - } - - dnp = diruser->po->np; - - mutex_lock (&dnp->lock); - - netfs_nref (dnp); /* acquire a reference for later netfs_nput */ - - do - { - assert (!lastcomp); - - /* Find the name of the next pathname component */ - nextname = index (filename, '/'); - - if (nextname) - { - *nextname++ = '\0'; - while (*nextname == '/') - nextname++; - if (*nextname == '\0') - { - /* These are the rules for filenames ending in /. */ - nextname = 0; - lastcomp = 1; - mustbedir = 1; - create = 0; - } - else - lastcomp = 0; - } - else - lastcomp = 1; - - np = 0; - - retry_lookup: - - if ((dnp == netfs_root_node || dnp == diruser->po->shadow_root) - && filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') - if (dnp == diruser->po->shadow_root) - /* We're at the root of a shadow tree. */ - { - *do_retry = FS_RETRY_REAUTH; - *retry_port = diruser->po->shadow_root_parent; - *retry_port_type = MACH_MSG_TYPE_COPY_SEND; - if (! lastcomp) - strcpy (retry_name, nextname); - error = 0; - mutex_unlock (&dnp->lock); - goto out; - } - else if (diruser->po->root_parent != MACH_PORT_NULL) - /* We're at a real translator root; even if DIRUSER->po has a - shadow root, we can get here if its in a directory that was - renamed out from under it... */ - { - *do_retry = FS_RETRY_REAUTH; - *retry_port = diruser->po->root_parent; - *retry_port_type = MACH_MSG_TYPE_COPY_SEND; - if (!lastcomp) - strcpy (retry_name, nextname); - error = 0; - mutex_unlock (&dnp->lock); - goto out; - } - else - /* We are global root */ - { - error = 0; - np = dnp; - netfs_nref (np); - } - else - /* Attempt a lookup on the next pathname component. */ - error = netfs_attempt_lookup_improved (diruser->user, dnp, - filename, &np, - flags, lastcomp, - retry_port, retry_port_type); - - /* At this point, DNP is unlocked */ - - /* Implement O_EXCL flag here */ - if (lastcomp && create && excl && !error && np) - error = EEXIST; - - /* Create the new node if necessary */ - if (lastcomp && create && error == ENOENT) - { - mode &= ~(S_IFMT | S_ISPARE | S_ISVTX); - mode |= S_IFREG; - mutex_lock (&dnp->lock); - - error = netfs_attempt_create_file_reduced (diruser->user, dnp, - filename, mode, flags); - - /* We retry lookup in two cases: - - we created the file and we have to get a valid port; - - someone has already created the file (between our lookup - and this create) then we just got EEXIST. If we are EXCL, - that's fine; otherwise, we have to retry the lookup. */ - if ((!error) || (error == EEXIST && !excl)) - { - mutex_lock (&dnp->lock); - goto retry_lookup; - } - - newnode = 1; - } - - /* All remaining errors get returned to the user */ - if (error) - goto out; - - if (np) - { - mutex_lock (&np->lock); - error = netfs_validate_stat (np, diruser->user); - mutex_unlock (&np->lock); - if (error) - goto out; - } - - if (np - && S_ISLNK (np->nn_translated) - && (!lastcomp - || mustbedir /* "foo/" must see that foo points to a dir */ - || !(flags & (O_NOLINK|O_NOTRANS)))) - { - size_t nextnamelen, newnamelen, linklen; - char *linkbuf; - - /* Handle symlink interpretation */ - if (nsymlinks++ > netfs_maxsymlinks) - { - error = ELOOP; - goto out; - } - - linklen = np->nn_stat.st_size; - - nextnamelen = nextname ? strlen (nextname) + 1 : 0; - newnamelen = nextnamelen + linklen + 1; - linkbuf = alloca (newnamelen); - - error = netfs_attempt_readlink (diruser->user, np, linkbuf); - if (error) - goto out; - - if (nextname) - { - linkbuf[linklen] = '/'; - memcpy (linkbuf + linklen + 1, nextname, - nextnamelen - 1); - } - linkbuf[nextnamelen + linklen] = '\0'; - - if (linkbuf[0] == '/') - { - /* Punt to the caller */ - *do_retry = FS_RETRY_MAGICAL; - *retry_port = MACH_PORT_NULL; - strcpy (retry_name, linkbuf); - goto out; - } - - filename = linkbuf; - if (lastcomp) - { - lastcomp = 0; - - /* Symlinks to nonexistent files aren't allowed to cause - creation, so clear the flag here. */ - create = 0; - } - netfs_nput (np); - mutex_lock (&dnp->lock); - np = 0; - } - else - { - /* Normal nodes here for next filename component */ - filename = nextname; - netfs_nrele (dnp); - - if (lastcomp) - dnp = 0; - else - { - dnp = np; - np = 0; - } - } - } - while (filename && *filename); - - /* At this point, NP is the node to return. */ - gotit: - - if (mustbedir && ! np) - { - error = ENOTDIR; - goto out; - } - - if (np) - error = netfs_check_open_permissions (diruser->user, np, - flags, newnode); - - if (error) - goto out; - - flags &= ~OPENONLY_STATE_MODES; - - if (np) - { - error = iohelp_dup_iouser (&user, diruser->user); - if (error) - goto out; - - newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), - user); - if (! newpi) - { - iohelp_free_iouser (user); - error = errno; - goto out; - } - - *retry_port = ports_get_right (newpi); - ports_port_deref (newpi); - } - - out: - if (np) - netfs_nput (np); - if (dnp) - netfs_nrele (dnp); - return error; -} - -/* Fill the array *DATA of size BUFSIZE with up to NENTRIES dirents - from DIR (which is locked) starting with entry ENTRY for user CRED. - The number of entries in the array is stored in *AMT and the number - of bytes in *DATACNT. If the supplied buffer is not large enough - to hold the data, it should be grown. */ -error_t -netfs_get_dirents (struct iouser *cred, struct node *dir, - int first_entry, int num_entries, char **data, - mach_msg_type_number_t *data_len, - vm_size_t max_data_len, int *data_entries) -{ - node_dirent_t *dirent_start, *dirent_current; - node_dirent_t *dirent_list = NULL; - size_t size = 0; - int count = 0; - char *data_p; - error_t err; - - int bump_size (const char *name) - { - if (num_entries == -1 || count < num_entries) - { - size_t new_size = size + DIRENT_LEN (strlen (name)); - - if (max_data_len > 0 && new_size > max_data_len) - return 0; - size = new_size; - count++; - return 1; - } - else - return 0; - } - - int add_dirent (const char *name, ino_t fileno, int type) - { - if (num_entries == -1 || count < num_entries) - { - struct dirent hdr; - size_t name_len = strlen (name); - size_t sz = DIRENT_LEN (name_len); - - if (sz > size) - return 0; - else - size -= sz; - - hdr.d_fileno = fileno; - hdr.d_reclen = sz; - hdr.d_type = type; - hdr.d_namlen = name_len; - - memcpy (data_p, &hdr, DIRENT_NAME_OFFS); - strcpy (data_p + DIRENT_NAME_OFFS, name); - data_p += sz; - count++; - - return 1; - } - else - return 0; - } - - err = node_entries_get (dir, &dirent_list); - - if (! err) - { - for (dirent_start = dirent_list, count = 2; - dirent_start && first_entry > count; - dirent_start = dirent_start->next, count++); - - count = 0; - - /* Make space for the `.' and `..' entries. */ - if (first_entry == 0) - bump_size ("."); - if (first_entry <= 1) - bump_size (".."); - - /* See how much space we need for the result. */ - for (dirent_current = dirent_start; - dirent_current; - dirent_current = dirent_current->next) - if (! bump_size (dirent_current->dirent->d_name)) - break; - - *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); - err = ((void *) *data == (void *) -1) ? errno : 0; - } - - if (! err) - { - data_p = *data; - *data_len = size; - *data_entries = count; - count = 0; - - /* Add `.' and `..' entries. */ - if (first_entry == 0) - add_dirent (".", 2, DT_DIR); - if (first_entry <= 1) - add_dirent ("..", 2, DT_DIR); - - for (dirent_current = dirent_start; - dirent_current; - dirent_current = dirent_current->next) - if (! add_dirent (dirent_current->dirent->d_name, - 2 /* FIXME */, - dirent_current->dirent->d_type)) - break; - } - - if (dirent_list) - node_entries_free (dirent_list); - - fshelp_touch (&dir->nn_stat, TOUCH_ATIME, maptime); - - return err; -} |