diff options
Diffstat (limited to 'netfs.c')
-rw-r--r-- | netfs.c | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/netfs.c b/netfs.c new file mode 100644 index 000000000..97653efbf --- /dev/null +++ b/netfs.c @@ -0,0 +1,834 @@ +/* tarfs interface to libnetfs. + Copyright (C) 2002 Ludovic Courtès <ludo@type-z.org> + + 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 */ + +#include <hurd.h> +#include <hurd/netfs.h> +#include <error.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include "backend.h" +#include "debug.h" + +/* BACKEND is defined in main.c */ +extern struct fs_backend backend; + +/* The following flag may be defined in order to hide files + to users who do not own them. */ +#ifdef HIDE_FILES_NOT_OWNED +#define OWNERSHIP(__node, __user) \ + fshelp_isowner (&(__node)->nn_stat, (__user)); +#else +# define OWNERSHIP(__node, __user) (0) +#endif /* HIDE_FILES_NOT_OWNED */ + +/* The user must define this function. Lookup NAME in DIR (which is + locked) for USER; set *NP to the found name upon return. If the + name was not found, then return ENOENT. On any error, clear *NP. + (*NP, if found, should be locked and a reference to it generated. + This call should unlock DIR no matter what.) */ +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **np) +{ + error_t err = 0; + + /* Lookups for "." and "..". */ + if (name[0] == '.' && + (name[1] == '\0' || + (name[1] == '.' && name[2] == '\0')) ) + /* Make sure that DIR is an actual directory. */ + if (S_ISDIR (dir->nn_stat.st_mode)) + { + if (name[1] == '.') + *np = dir->nn->dir; + else + *np = dir; + } + else + { + *np = NULL; + err = ENOTDIR; + } + else + /* Regular nodes. */ + err = backend.lookup_node (np, dir, name); + + /* Create a reference to the node (and lock it); unlock DIR. */ + if (!err && *np) + { + if (*np != dir) + mutex_lock (&(*np)->lock); + + debug (("Node %s: %i references", name, (*np)->references)); + netfs_nref (*np); + } + + mutex_unlock (&dir->lock); + + return err; +} + +/* The user must define this function. 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) +{ + if ((!buf) || (!np->nn->symlink)) + return EAGAIN; /* This should never happen. */ + + strcpy (buf, np->nn->symlink); + + return 0; +} + +/* The user must define this function. Locked node NP is being opened + by USER, with FLAGS. NEWNODE is nonzero if we just created this + node. Return an error if we should not permit the open to complete + because of a permission restriction. */ +error_t +netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = OWNERSHIP (np, user); + + 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; +} + +/* The user must define this function. 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, + loff_t offset, size_t *len, void *data) +{ + return backend.read_node (np, offset, len, data); +} + + +/* The user must define this function. 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, + loff_t offset, size_t *len, void *data) +{ + if (! backend.write_node) + return EROFS; + else + return backend.write_node (np, offset, len, data); +} + +/* The user must define this function. Return the valid access + types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for + locked file NODE and user CRED. */ +error_t +netfs_report_access (struct iouser *cred, struct node *node, + int *types) +{ + error_t err = OWNERSHIP (node, cred); + + /* FIXME: For a ro-fs, this should only set TYPES to O_READ. */ + if (! err) + { + *types = 0; + if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + } + + return err; +} + +/* The user must define this function. 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) +{ + debug (("Not implemented")); + return NULL; +} + +/* The user must define this function. Node NODE has no more references; + free all its associated storage. */ +void +netfs_node_norefs (struct node *node) +{ + debug (("Entering")); + + backend.free_node (node); +} + +/* The user must define this function. Fill the array *DATA of size + BUFSIZE slast 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 entry, int nentries, char **data, + mach_msg_type_number_t *datacnt, + vm_size_t bufsize, int *amt) +{ + int curr_entry; /* current entry */ + static int curr_amt; + struct dirent* curr_dirent; + char* curr_datap; /* current position in DATA */ + int no_more = 0; /* no more entries? */ + + curr_amt = 0; + curr_datap = *data; + + /* Start with entry ENTRY */ + backend.set_curr_dir (dir); + no_more = backend.skip_entries (entry); + + for (curr_entry = entry; + !no_more; + curr_entry++) + { + /* No limitiation when NENTRIES==-1. */ + if ((nentries >= 0) && (curr_entry > entry+nentries)) + no_more = 1; + else + no_more = backend.get_next_entry(&curr_dirent); + + if (!no_more) + { +#ifdef HIDE_FILES_NOT_OWNED + /* FIXME: We should do something here to avoid the ENOENT + during a dir_lookup () after a dir_readdir (). */ +#endif + curr_amt++; + + /* Grow the buffer pointed to by DATA if necessary. */ + if (((curr_datap - *data) + curr_dirent->d_reclen) > bufsize) + { + void* newdata; + size_t prev_size = bufsize; + + /* Makes BUFSIZE a multiple of VM_PAGE_SIZE or double it. */ + if (!bufsize) + bufsize = vm_page_size; + else + bufsize = (bufsize%vm_page_size) ? + ((bufsize/vm_page_size)+vm_page_size): + (bufsize*2); + + newdata = mmap (0, bufsize, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS, 0, 0); + assert (newdata != (void*)-1); + assert (newdata != NULL); + + if (newdata != *data) + { + size_t s = curr_datap - *data; + memcpy (newdata, (void*) *data, s); + curr_datap = (char*)newdata + s; + + munmap (*data, prev_size); + *data = (char*)newdata; + } + } + assert (*data != NULL); + + /* Copy CURR_DIRENT into DATA. */ + memcpy (curr_datap, curr_dirent, curr_dirent->d_reclen); + curr_datap += curr_dirent->d_reclen; + munmap (curr_dirent, curr_dirent->d_reclen); + } + } + +#if 0 + /* Deallocate if necessary. */ + if (bufsize > (curr_datap - *data)) + munmap (curr_datap, bufsize - (curr_datap - *data)); +#endif + + /* Return */ + *amt = curr_amt; + *datacnt = curr_datap - *data; + + return 0; +} + +/* The user may define this function. For a full description, + see hurd/hurd_types.h. The default response indicates a network + store. If the supplied buffers are not large enough, they should + be grown as necessary. NP is locked. */ +error_t +netfs_file_get_storage_info (struct iouser *cred, + struct node *np, + mach_port_t **ports, + mach_msg_type_name_t *ports_type, + mach_msg_type_number_t *num_ports, + int **ints, + mach_msg_type_number_t *num_ints, + loff_t **offsets, + mach_msg_type_number_t *num_offsets, + char **data, + mach_msg_type_number_t *data_len) +{ +#ifdef DEBUG + error (0, 0, __FUNCTION__); +#endif + return EOPNOTSUPP; +} + +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t +netfs_validate_stat (struct node *np, struct iouser *cred) +{ + return OWNERSHIP (np, cred); +} + +/* The user must define this function. 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) +{ + error_t err = 0; + + if (backend.change_stat) + { + int flags = TOUCH_CTIME; + io_statbuf_t st = np->nn_stat; + + err = fshelp_isowner (&np->nn_stat, cred); + + if (! err) + { + if (atime) + { + st.st_atime = atime->tv_sec; + st.st_atime_usec = atime->tv_nsec / 1000; + } + else + flags |= TOUCH_ATIME; + + if (mtime) + { + st.st_mtime = mtime->tv_sec; + st.st_mtime_usec = mtime->tv_nsec / 1000; + } + else + flags |= TOUCH_MTIME; + + err = backend.change_stat (np, &st); + } + } + else + err = EROFS; + + return err; +} + +/* The user must define this function. 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, + loff_t size) +{ + error_t err = 0; + + if (backend.change_stat) + { + io_statbuf_t st = np->nn_stat; + st.st_size = size; + backend.change_stat (np, &st); + } + else + err = EROFS; + + return err; +} + +/* The user must define this function. 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, + fsys_statfsbuf_t *st) +{ +#ifdef DEBUG + error (0, 0, __FUNCTION__); +#endif + return EOPNOTSUPP; +} + +/* The user must define this function. 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) +{ +#ifdef DEBUG + error (0, 0, __FUNCTION__); +#endif + return EOPNOTSUPP; +} + +/* The user must define this function. 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) +{ + error_t err = 0; + + if (backend.sync_fs) + { + if (cred) + { + err = fshelp_isowner (&netfs_root_node->nn_stat, cred); + if (err) + return err; + err = backend.sync_fs (wait); + } + else + /* From libnetfs source code, CRED is set to zero in the fsys-goaway + stub, so we call go_away () here. */ + if (backend.go_away) + err = backend.go_away (); /* This should call sync_fs () */ + else + err = backend.sync_fs (wait); + } + else + err = EOPNOTSUPP; + + return err; +} + +/* The user may define this function. 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; +} + +/* The user may define this function (but should define it together + with netfs_set_translator). 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) +{ + *argz_len = 0; + *argz = (char*)malloc (sizeof (char)); + (*argz)[0] = '\0'; + + return 0; +} + +/* The user must define this function. 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) +{ + error_t err = 0; + + if (backend.change_stat) + { + io_statbuf_t st; + + err = fshelp_isowner (&np->nn_stat, cred); + + if (! err) + { + st = np->nn_stat; + st.st_uid = uid; + st.st_gid = gid; + err = backend.change_stat (np, &st); + } + } + else + err = EROFS; + + return err; +} + +/* The user must define this function. 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) +{ + error_t err = 0; + + if (backend.change_stat) + { + io_statbuf_t st; + + err = fshelp_isowner (&np->nn_stat, cred); + + if (! err) + { + st = np->nn_stat; + st.st_author = author; + err = backend.change_stat (np, &st); + } + } + else + err = EROFS; + + return err; +} + +/* The user must define this function. 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) +{ + error_t err = 0; + + if (backend.change_stat) + { + io_statbuf_t st; + + err = fshelp_isowner (&np->nn_stat, cred); + + if (! err) + { + st = np->nn_stat; + + if (mode & S_IFMT) + { + /* User wants to change file type, check whether this is possible */ + if (S_ISDIR (st.st_mode) || S_ISDIR (mode)) + { + /* Any->Dir and Dir->Any are forbidden transitions */ + if ((st.st_mode & S_IFMT) != (mode & S_IFMT)) + err = EOPNOTSUPP; + } + else + /* Let him do it */ + st.st_mode = 0; + } + else + /* Only clear the permission bits */ + st.st_mode &= ~S_ISPARE; + + if (!err) + { + st.st_mode |= mode; + err = backend.change_stat (np, &st); + } + } + } + else + err = EROFS; + + return err; +} + +/* The user must define this function. 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) +{ + error_t err; + + if (backend.symlink_node) + { + err = fshelp_isowner (&np->nn_stat, cred); + /* FIXME: Call fshelp_access () ?! */ + + if (! err) + err = backend.symlink_node (np, name); + } + else + err = EOPNOTSUPP; + + return err; +} + +/* The user must define this function. 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) +{ + error_t err; + + if (backend.mkdev_node) + { + err = fshelp_isowner (&np->nn_stat, cred); /* FIXME: See above */ + + if (! err) + err = backend.mkdev_node (np, type, indexes); + } + else + err = EOPNOTSUPP; + + return err; +} + +/* The user must define this function. 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) +{ + debug (("Not implemented")); + return EOPNOTSUPP; +} + +/* The user must define this function. 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; + + if (backend.unlink_node) + { + struct node *node; + + err = backend.lookup_node (&node, dir, name); + //usleep (500); /* FIXME!!! The incredible race condition! */ + + if (!err) + { + mutex_lock (&node->lock); + + debug (("Node %s: %i references", name, node->references)); + err = fshelp_isowner (&node->nn_stat, user); + + if (!err) + err = backend.unlink_node (node); + + mutex_unlock (&node->lock); + } + } + else + err = EROFS; + + return err; +} + +/* The user must define this function. 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) +{ + debug (("FIXME: Not implemented")); + return EOPNOTSUPP; +} + +/* The user must define this function. 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 = fshelp_isowner (&dir->nn_stat, user); + struct node *newdir; + + if (!backend.create_node) + err = EROFS; + else + err = backend.create_node (&newdir, dir, name, mode); + + return err; +} + + +/* The user must define this function. 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) +{ + /* Simply redirect the call */ + return netfs_attempt_unlink (user, dir, name); +} + + +/* The user must define this function. 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) +{ + error_t err; + + if (backend.link_node) + { + err = fshelp_isowner (&dir->nn_stat, user); + if (!err) + err = backend.link_node (dir, file, name, excl); + } + else + err = EROFS; + + return err; +} + + +/* The user must define this function. 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) +{ + /* Redirect the call */ + return netfs_attempt_create_file (user, dir, NULL, mode, np); +} + + +/* The user must define 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) +{ + error_t err = fshelp_isowner (&dir->nn_stat, user); + + if (!backend.create_node) + { + err = EROFS; + *np = NULL; + } + else + { + /* Note: create_node () must handle nameless node creation + (see netfs_attempt_mkfile ()). */ + err = backend.create_node (np, dir, name, mode); + + /* Lock the new node and add a reference to it on success. */ + if (!err && *np) + { + debug (("Node %s: %i references", name, (*np)->references)); + mutex_lock (&(*np)->lock); + netfs_nref (*np); + } + } + + mutex_unlock (&dir->lock); + + return err; +} + +/* Append to the malloced string *ARGZ of length *ARGZ_LEN a NUL-separated + list of the arguments to this translator. The default definition of this + routine simply calls netfs_append_std_options. */ +error_t +netfs_append_args (char **argz, unsigned *argz_len) +{ + error_t err = 0; + + if (backend.get_args) + err = backend.get_args (argz, argz_len); + + return err; +} + +/* Parse and execute the runtime options in ARGZ & ARGZ_LEN. EINVAL is + returned if some option is unrecognized. The default definition of this + routine will parse them using NETFS_RUNTIME_ARGP. */ +error_t +netfs_set_options (char *argz, size_t argz_len) +{ + error_t err = EINVAL; + + if (backend.set_options) + err = backend.set_options (argz, argz_len); + + return err; +} + + +/* We need a particular syncfs stub that doesn't lock the NODE + which was passed to file_syncfs () since we want to lock it + in tarfs_sync_fs (). */ +error_t +netfs_S_file_syncfs (struct protid *user, + int wait, + int dochildren) +{ + error_t err; + + if (!user) + return EOPNOTSUPP; + + err = netfs_attempt_syncfs (user->user, wait); + + return err; +} + +/* The following stub has been added as a reminder. */ +#if 0 +error_t +netfs_S_io_map (struct protid *user, + mach_port_t *rdobj, mach_msg_type_name_t *rdobjtype, + mach_port_t *wrobj, mach_msg_type_name_t *wrobjtype) +{ + error (0, 0, "Warning: io_map () not supported"); + return EOPNOTSUPP; +} +#endif |