diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index 3c561cf5c..432512a17 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -1112,6 +1112,9 @@ ./usr/lib/libtermlib.so minix-base ./usr/lib/libtermlib.so.0 minix-base ./usr/lib/libtermlib.so.0.6 minix-base +./usr/lib/libukfs.so.1.0 minix-base rump +./usr/lib/libukfs.so.1 minix-base rump +./usr/lib/libukfs.so minix-base rump ./usr/lib/libutil.so minix-base ./usr/lib/libutil.so.7 minix-base ./usr/lib/libutil.so.7.21 minix-base obsolete diff --git a/distrib/sets/lists/minix-comp/mi b/distrib/sets/lists/minix-comp/mi index e5510b432..135d906fa 100644 --- a/distrib/sets/lists/minix-comp/mi +++ b/distrib/sets/lists/minix-comp/mi @@ -1735,6 +1735,7 @@ ./usr/include/rump/rumpuser_port.h minix-comp rump ./usr/include/rump/rumpvfs_if_pub.h minix-comp rump ./usr/include/rump/rumpvnode_if.h minix-comp rump +./usr/include/rump/ukfs.h minix-comp rump ./usr/include/saslc.h minix-comp crypto ./usr/include/sched.h minix-comp ./usr/include/search.h minix-comp @@ -2458,6 +2459,7 @@ ./usr/lib/libtermlib_pic.a minix-comp ./usr/lib/libtimers.a minix-comp ./usr/lib/libtimers_pic.a minix-comp +./usr/lib/libukfs.a minix-comp rump ./usr/lib/libusb.a minix-comp ./usr/lib/libusb_pic.a minix-comp ./usr/lib/libutil.a minix-comp diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index 47ada63ed..afb96576b 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -3070,6 +3070,7 @@ ./usr/man/man3/tzsetwall.3 minix-man ./usr/man/man3/ualarm.3 minix-man ./usr/man/man3/uid_from_user.3 minix-man +./usr/man/man3/ukfs.3 minix-man rump ./usr/man/man3/ulimit.3 minix-man ./usr/man/man3/uname.3 minix-man ./usr/man/man3/unctrl.3 minix-man diff --git a/lib/Makefile b/lib/Makefile index 42d4cdf71..7e50317a1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -307,7 +307,7 @@ SUBDIR+= ../external/bsd/mdocml/lib #==================== 4th library dependency barrier ==================== SUBDIR+= .WAIT -#SUBDIR+= libukfs # depends on librumpvfs, librump +SUBDIR+= libukfs # depends on librumpvfs, librump .if (${MKTPM} != "no") SUBDIR+= ../crypto/external/cpl/trousers/lib diff --git a/lib/libukfs/Makefile b/lib/libukfs/Makefile new file mode 100644 index 000000000..f06798488 --- /dev/null +++ b/lib/libukfs/Makefile @@ -0,0 +1,26 @@ +# $NetBSD: Makefile,v 1.7 2013/09/11 23:04:11 joerg Exp $ +# + +WARNS= 2 + +LIB= ukfs +.if defined(__MINIX) +LIBDPLIBS+= rump ${.CURDIR}/../librump \ + rumpvfs ${.CURDIR}/../librumpvfs \ + mthread ${.CURDIR}/../../minix/lib/libmthread + +CPPFLAGS+= -Wno-unused-function +.else +LIBDPLIBS+= rump ${.CURDIR}/../librump \ + rumpvfs ${.CURDIR}/../librumpvfs \ + pthread ${.CURDIR}/../libpthread +.endif # defined(__MINIX) +CPPFLAGS+= -I${.CURDIR} + +INCS= ukfs.h +INCSDIR= /usr/include/rump + +SRCS= ukfs.c ukfs_disklabel.c +MAN= ukfs.3 + +.include diff --git a/lib/libukfs/shlib_version b/lib/libukfs/shlib_version new file mode 100644 index 000000000..398b9635d --- /dev/null +++ b/lib/libukfs/shlib_version @@ -0,0 +1,4 @@ +# $NetBSD: shlib_version,v 1.2 2009/01/11 02:47:19 christos Exp $ +# +major=1 +minor=0 diff --git a/lib/libukfs/ukfs.3 b/lib/libukfs/ukfs.3 new file mode 100644 index 000000000..5876b1169 --- /dev/null +++ b/lib/libukfs/ukfs.3 @@ -0,0 +1,321 @@ +.\" $NetBSD: ukfs.3,v 1.14 2014/02/14 07:27:37 wiz Exp $ +.\" +.\" Copyright (c) 2008 Antti Kantee. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd February 13, 2014 +.Dt UKFS 3 +.Os +.Sh NAME +.Nm ukfs +.Nd user kernel file system library interface +.Sh LIBRARY +ukfs Library (libukfs, \-lukfs) +.Sh SYNOPSIS +.In rump/ukfs.h +.Sh DESCRIPTION +The +.Nm +library provides direct access to file systems without having to +specially mount a file system. +Therefore, accessing a file system through +.Nm +requires no special kernel support apart from standard POSIX functionality. +As +.Nm +is built upon +.Xr rump 3 +kernels, all kernel file systems which are supported by rump kernels +are available. +It allows to write utilities for accessing file systems +without having to duplicate file system internals knowledge already +present in kernel file system drivers. +.Pp +.Nm +provides a high-level pathname based interface for accessing file systems. +If a lower level interface it desired, +.Xr rump 3 +kernels should be used directly. +However, much like system calls, the interfaces of +.Nm , +are self-contained and require no tracking and release of resources. +The only exception is the file system handle +.Ft struct ukfs +which should be released after use. +.Sh INITIALIZATION +.Bl -ohang +.It Ft int +.Fn ukfs_init +.It Ft int +.Fn ukfs_modload "const char *fname" +.It Ft int +.Fn ukfs_modload_dir "const char *dirname" +.It Ft ssize_t +.Fn ukfs_vfstypes "char *buf" "size_t buflen" +.It Ft struct ukfs * +.Fn ukfs_mount "const char *vfsname" "const char *devpath" \ +"const char *mountpath" "int mntflags" "void *arg" "size_t alen" +.It Ft struct ukfs * +.Fn ukfs_mount_disk "const char *vfsname" "const char *devpath" \ +"int partition" "const char *mountpath" "int mntflags" \ +"void *arg" "size_t alen" +.It Ft int +.Fn ukfs_release "struct ukfs *ukfs" "int flags" +.El +.Pp +.Fn ukfs_init +intializes the library and must be called once per process using +.Nm . +.Pp +.Fn ukfs_modload +is used at runtime to dynamically load a library which contains a +file system module. +For this to succeed, the +.Xr rump 3 +kernel and the module targetted must be compiled with compatible kernel +versions and the application must be dynamically linked. +Additionally, since this routine does not handle dependencies, all the +dependencies of the library must be loaded beforehand. +The routine returns \-1 for fatal error, 0 for dependency failure and 1 +for success. +.Pp +.Fn ukfs_modload_dir +loads all +.Xr rump 3 +kernel file system components in directory +.Fa dirname . +It looks for libraries which begin with +.Pa librumpfs_ +and end in +.Pa .so . +The routine tries to handle dependencies by retrying to load libraries +which failed due to dependencies. +.Fn ukfs_modload_dir +returns the number of vfs modules loaded or sets errno and +returns \-1 in case of a fatal error in directory searching. +In case a fatal error occurs after some modules have already been +loaded, the number of loaded module is returned. +Fatal errors in loading the modules themselves are ignored and +.Fn ukfs_modload +should be used directly if finegrained error reporting is desired. +.Pp +It should be noted that the above routines affect the whole process, +not just a specific instance of +.Nm . +It is preferable to call them from only one thread, as the underlying +dynamic library interfaces may not be threadsafe. +.Pp +.Fn ukfs_vfstypes +queries the available file system types and returns a nul-terminated +list of types separated by spaces in +.Fa buf . +The format of the list is equivalent to the one returned by +.Xr sysctl 3 +on the name +.Pa vfs.generic.fstypes . +The function returns the length of the string without the trailing nul +or \-1 for error. +Notably, the return value 0 means there are no file systems available. +If there is not enough room in the caller's buffer for all file system +types, as many as fit will be returned. +.Pp +.Fn ukfs_mount +intializes a file system image. +The handle resulting from the operation is passed to all other routines +and identifies the instance of the mount analoguous to what a pathname +specifies in a normally mounted file system. +The parameters are the following: +.Bl -tag -width XXX -offset indent +.It vfsname +Name of the file system to be used, e.g. +.Dv MOUNT_FFS . +.It devpath +Path of file system image. +It can be either a regular file, device or, if the file system does +not support the concept of a device, an abitrary string, e.g. network +address. +.It mountpath +Path where the file system is mounted to. +This parameter is used only by the file system being mounted. +Most of the time +.Dv UKFS_DEFAULTMP +is the correct path. +.It mntflags +Flags as passed to the +.Xr mount 2 +system call, for example +.Dv MNT_RDONLY . +In addition to generic parameters, file system specific parameters such as +.Dv MNT_LOG +(ffs) may be passed here. +.It arg +File system private argument structure. +This is passed directly to the file system. +It must match what +.Fa vfsname +expects. +.It alen +Size of said structure. +.El +.Pp +The +.Fn ukfs_mount_disk +function must be used to mount disk-based file systems. +It takes the same arguments as +.Fn ukfs_mount , +except for an additional argument signifying the +.Fa partition +number. +If the image +.Fa devpath +contains a disklabel, this value specifies the number of the partition +within the image used as the file system backend. +If +.Fa devpath +does not contain a disklabel, the value +.Dv UKFS_PARTITION_NONE +must be used to signal that the file system backend is the entire +image. +.Pp +.Fn ukfs_release +unmounts the file system and releases the resources associated with +.Fa ukfs . +The return value signals the return value of the unmount operation. +If non-zero, +.Fa ukfs +will continue to remain valid. +The possible values for flags are: +.Bl -tag -width XUKFS_RELFLAG_NOUNMOUT -offset indent +.It Dv UKFS_RELFLAG_NOUNMOUNT +Do not unmount file system, just release ukfs handle. +Release always succeeds. +.It Dv UKFS_RELFLAG_FORCE +Forcefully unmount the file system. +This means that any busy nodes (due to e.g. +.Fn ukfs_chdir ) +will be ignored. +Release always succeeds. +.El +.Sh OPERATION +.Bl -ohang +.It Ft int +.Fn ukfs_chdir "struct ukfs *ukfs" "const char *path" +.It Ft int +.Fn ukfs_getdents "struct ukfs *ukfs" "const char *dirname" "off_t *off" \ +"uint8_t *buf" "size_t bufsize" +.It Ft ssize_t +.Fn ukfs_read "struct ukfs *ukfs" "const char *filename" "off_t off" \ +"uint8_t *buf" "size_t bufsize" +.It Ft ssize_t +.Fn ukfs_write "struct ukfs *ukfs" "const char *filename" "off_t off" \ +"uint8_t *buf" "size_t bufsize" +.It Ft int +.Fn ukfs_create "struct ukfs *ukfs" "const char *filename" "mode_t mode" +.It Ft int +.Fn ukfs_mknod "struct ukfs *ukfs" "const char *path" "mode_t mode" "dev_t dev" +.It Ft int +.Fn ukfs_mkfifo "struct ukfs *ukfs" "const char *path" "mode_t mode" +.It Ft int +.Fn ukfs_mkdir "struct ukfs *ukfs" "const char *filename" "mode_t mode" +.It Ft int +.Fn ukfs_remove "struct ukfs *ukfs" "const char *filename" +.It Ft int +.Fn ukfs_rmdir "struct ukfs *ukfs" "const char *filename" +.It Ft int +.Fn ukfs_link "struct ukfs *ukfs" "const char *filename" "const char *f_create" +.It Ft int +.Fn ukfs_symlink "struct ukfs *ukfs" "const char *filename" \ +"const char *linkname" +.It Ft ssize_t +.Fn ukfs_readlink "struct ukfs *ukfs" "const char *filename" \ +"char *linkbuf" "size_t buflen" +.It Ft int +.Fn ukfs_rename "struct ukfs *ukfs" "const char *from" "const char *to" +.It Ft int +.Fn ukfs_stat "struct ukfs *ukfs" "const char *filename" \ +"struct stat *file_stat" +.It Ft int +.Fn ukfs_lstat "struct ukfs *ukfs" "const char *filename" \ +"struct stat *file_stat" +.It Ft int +.Fn ukfs_chmod "struct ukfs *ukfs" "const char *filename" "mode_t mode" +.It Ft int +.Fn ukfs_lchmod "struct ukfs *ukfs" "const char *filename" "mode_t mode" +.It Ft int +.Fn ukfs_chown "struct ukfs *ukfs" "const char *filename" "uid_t uid" \ +"gid_t gid" +.It Ft int +.Fn ukfs_lchown "struct ukfs *ukfs" "const char *filename" "uid_t uid" \ +"gid_t gid" +.It Ft int +.Fn ukfs_chflags "struct ukfs *ukfs" "const char *filename" "u_long flags" +.It Ft int +.Fn ukfs_lchflags "struct ukfs *ukfs" "const char *filename" "u_long flags" +.It Ft int +.Fn ukfs_utimes "struct ukfs *ukfs" "const char *filename" \ +"const struct timeval *tptr" +.It Ft int +.Fn ukfs_lutimes "struct ukfs *ukfs" "const char *filename" \ +"const struct timeval *tptr" +.El +.Pp +The above routines operate like their system call counterparts and the +system call manual pages without the ukfs_ prefix should be referred to +for further information on the parameters. +.Pp +The only call which modifies +.Fa ukfs +state is +.Fn ukfs_chdir . +It works like +.Xr chdir 2 +in the sense that it affects the interpretation of relative paths. +If succesful, all relative pathnames will be resolved starting from the +current directory. +Currently the call affects all accesses to that particular +.Fa ukfs , +but it might be later changed to be thread private. +.Sh UTILITIES +.Bl -ohang +.It Ft int +.Fn ukfs_util_builddirs "struct ukfs *ukfs" "const char *pathname" "mode_t mode" +.El +.Pp +Builds a directory hierarchy. +Unlike mkdir, the +.Fa pathname +argument may contain multiple levels of hierarchy. +It is not considered an error if any of the directories specified exist +already. +.Sh SEE ALSO +.Xr rump 3 +.Sh HISTORY +.Nm +first appeared in +.Nx 5.0 . +.Sh AUTHORS +.An Antti Kantee Aq Mt pooka@cs.hut.fi +.Sh NOTES +.Nm +was an early attempt at an interface for kernel file systems +running in userspace. diff --git a/lib/libukfs/ukfs.c b/lib/libukfs/ukfs.c new file mode 100644 index 000000000..f661a57d0 --- /dev/null +++ b/lib/libukfs/ukfs.c @@ -0,0 +1,1379 @@ +/* $NetBSD: ukfs.c,v 1.58 2015/06/17 00:15:26 christos Exp $ */ + +/* + * Copyright (c) 2007, 2008, 2009 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by the + * Finnish Cultural Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This library enables access to files systems directly without + * involving system calls. + */ + +#ifdef __linux__ +#define _XOPEN_SOURCE 500 +#define _BSD_SOURCE +#define _FILE_OFFSET_BITS 64 +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#if defined(__minix) +#define _MTHREADIFY_PTHREADS +#include +#else +#include +#endif /* defined(__minix) */ +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "ukfs_int_disklabel.h" + +#define UKFS_MODE_DEFAULT 0555 + +struct ukfs { + pthread_spinlock_t ukfs_spin; + + struct mount *ukfs_mp; + struct lwp *ukfs_lwp; + void *ukfs_specific; + + int ukfs_devfd; + + char *ukfs_devpath; + char *ukfs_mountpath; + char *ukfs_cwd; + + struct ukfs_part *ukfs_part; +}; + +static int builddirs(const char *, mode_t, + int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *); + +struct mount * +ukfs_getmp(struct ukfs *ukfs) +{ + + return ukfs->ukfs_mp; +} + +void +ukfs_setspecific(struct ukfs *ukfs, void *priv) +{ + + ukfs->ukfs_specific = priv; +} + +void * +ukfs_getspecific(struct ukfs *ukfs) +{ + + return ukfs->ukfs_specific; +} + +#ifdef DONT_WANT_PTHREAD_LINKAGE +#define pthread_spin_lock(a) +#define pthread_spin_unlock(a) +#define pthread_spin_init(a,b) +#define pthread_spin_destroy(a) +#endif + +static int +precall(struct ukfs *ukfs, struct lwp **curlwp) +{ + + /* save previous. ensure start from pristine context */ + *curlwp = rump_pub_lwproc_curlwp(); + if (*curlwp) + rump_pub_lwproc_switch(ukfs->ukfs_lwp); + rump_pub_lwproc_rfork(RUMP_RFCFDG); + + if (rump_sys_chroot(ukfs->ukfs_mountpath) == -1) + return errno; + if (rump_sys_chdir(ukfs->ukfs_cwd) == -1) + return errno; + + return 0; +} + +static void +postcall(struct lwp *curlwp) +{ + + rump_pub_lwproc_releaselwp(); + if (curlwp) + rump_pub_lwproc_switch(curlwp); +} + +#define PRECALL() \ +struct lwp *ukfs_curlwp; \ +do { \ + int ukfs_rv; \ + if ((ukfs_rv = precall(ukfs, &ukfs_curlwp)) != 0) { \ + errno = ukfs_rv; \ + return -1; \ + } \ +} while (/*CONSTCOND*/0) + +#define POSTCALL() postcall(ukfs_curlwp); + +struct ukfs_part { + pthread_spinlock_t part_lck; + int part_refcount; + + int part_type; + char part_labelchar; + off_t part_devoff; + off_t part_devsize; +}; + +enum ukfs_parttype { UKFS_PART_NONE, UKFS_PART_DISKLABEL, UKFS_PART_OFFSET }; + +static struct ukfs_part ukfs__part_none = { + .part_type = UKFS_PART_NONE, + .part_devoff = 0, + .part_devsize = RUMP_ETFS_SIZE_ENDOFF, +}; +static struct ukfs_part ukfs__part_na; +struct ukfs_part *ukfs_part_none = &ukfs__part_none; +struct ukfs_part *ukfs_part_na = &ukfs__part_na; + +#define PART2LOCKSIZE(len) ((len) == RUMP_ETFS_SIZE_ENDOFF ? 0 : (len)) + +int +_ukfs_init(int version) +{ + int rv; + + if (version != UKFS_VERSION) { + errno = EPROGMISMATCH; + warn("incompatible ukfs version, %d vs. %d", + version, UKFS_VERSION); + return -1; + } + + if ((rv = rump_init()) != 0) { + errno = rv; + return -1; + } + + return 0; +} + +/*ARGSUSED*/ +static int +rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode) +{ + + return rump_sys_mkdir(path, mode); +} + +int +ukfs_part_probe(char *devpath, struct ukfs_part **partp) +{ + struct ukfs_part *part; + char *p; + int error = 0; + int devfd = -1; + + if ((p = strstr(devpath, UKFS_PARTITION_SCANMAGIC)) != NULL) { + warnx("ukfs: %%PART is deprecated. use " + "%%DISKLABEL instead"); + errno = ENODEV; + return -1; + } + + part = malloc(sizeof(*part)); + if (part == NULL) { + errno = ENOMEM; + return -1; + } + if (pthread_spin_init(&part->part_lck, PTHREAD_PROCESS_PRIVATE) == -1) { + error = errno; + free(part); + errno = error; + return -1; + } + part->part_type = UKFS_PART_NONE; + part->part_refcount = 1; + + /* + * Check for magic in pathname: + * disklabel: /regularpath%DISKLABEL:labelchar%\0 + * offsets: /regularpath%OFFSET:start,end%\0 + */ +#define MAGICADJ_DISKLABEL(p, n) (p+sizeof(UKFS_DISKLABEL_SCANMAGIC)-1+n) + if ((p = strstr(devpath, UKFS_DISKLABEL_SCANMAGIC)) != NULL + && strlen(p) == UKFS_DISKLABEL_MAGICLEN + && *(MAGICADJ_DISKLABEL(p,1)) == '%') { + if (*(MAGICADJ_DISKLABEL(p,0)) >= 'a' && + *(MAGICADJ_DISKLABEL(p,0)) < 'a' + UKFS_MAXPARTITIONS) { + struct ukfs__disklabel dl; + struct ukfs__partition *pp; + int imswapped; + char buf[65536]; + char labelchar = *(MAGICADJ_DISKLABEL(p,0)); + int partition = labelchar - 'a'; + uint32_t poffset, psize; + + *p = '\0'; + devfd = open(devpath, O_RDONLY); + if (devfd == -1) { + error = errno; + goto out; + } + + /* Locate the disklabel and find the partition. */ + if (pread(devfd, buf, sizeof(buf), 0) == -1) { + error = errno; + goto out; + } + + if (ukfs__disklabel_scan(&dl, &imswapped, + buf, sizeof(buf)) != 0) { + error = ENOENT; + goto out; + } + + if (dl.d_npartitions < partition) { + error = ENOENT; + goto out; + } + + pp = &dl.d_partitions[partition]; + part->part_type = UKFS_PART_DISKLABEL; + part->part_labelchar = labelchar; + if (imswapped) { + poffset = bswap32(pp->p_offset); + psize = bswap32(pp->p_size); + } else { + poffset = pp->p_offset; + psize = pp->p_size; + } + part->part_devoff = poffset << DEV_BSHIFT; + part->part_devsize = psize << DEV_BSHIFT; + } else { + error = EINVAL; + } +#define MAGICADJ_OFFSET(p, n) (p+sizeof(UKFS_OFFSET_SCANMAGIC)-1+n) + } else if (((p = strstr(devpath, UKFS_OFFSET_SCANMAGIC)) != NULL) + && (strlen(p) >= UKFS_OFFSET_MINLEN)) { + char *comma, *pers, *ep, *nptr; + u_quad_t val; + + comma = strchr(p, ','); + if (comma == NULL) { + error = EINVAL; + goto out; + } + pers = strchr(comma, '%'); + if (pers == NULL) { + error = EINVAL; + goto out; + } + *comma = '\0'; + *pers = '\0'; + *p = '\0'; + + nptr = MAGICADJ_OFFSET(p,0); + /* check if string is negative */ + if (*nptr == '-') { + error = ERANGE; + goto out; + } + val = strtouq(nptr, &ep, 10); + if (val == UQUAD_MAX) { + error = ERANGE; + goto out; + } + if (*ep != '\0') { + error = EADDRNOTAVAIL; /* creative ;) */ + goto out; + } + part->part_devoff = val; + + /* omstart */ + + nptr = comma+1; + /* check if string is negative */ + if (*nptr == '-') { + error = ERANGE; + goto out; + } + val = strtouq(nptr, &ep, 10); + if (val == UQUAD_MAX) { + error = ERANGE; + goto out; + } + if (*ep != '\0') { + error = EADDRNOTAVAIL; /* creative ;) */ + goto out; + } + part->part_devsize = val; + part->part_type = UKFS_PART_OFFSET; + } else { + ukfs_part_release(part); + part = ukfs_part_none; + } + + out: + if (devfd != -1) + close(devfd); + if (error) { + free(part); + errno = error; + } else { + *partp = part; + } + + return error ? -1 : 0; +} + +int +ukfs_part_tostring(struct ukfs_part *part, char *str, size_t strsize) +{ + int rv; + + *str = '\0'; + /* "pseudo" values */ + if (part == ukfs_part_na) { + errno = EINVAL; + return -1; + } + if (part == ukfs_part_none) + return 0; + + rv = 0; + switch (part->part_type) { + case UKFS_PART_NONE: + break; + + case UKFS_PART_DISKLABEL: + snprintf(str, strsize, "%%DISKLABEL:%c%%",part->part_labelchar); + rv = 1; + break; + + case UKFS_PART_OFFSET: + snprintf(str, strsize, "[%llu,%llu]", + (unsigned long long)part->part_devoff, + (unsigned long long)(part->part_devoff+part->part_devsize)); + rv = 1; + break; + } + + return rv; +} + +static void +unlockdev(int fd, struct ukfs_part *part) +{ + struct flock flarg; + + if (part == ukfs_part_na) + return; + + memset(&flarg, 0, sizeof(flarg)); + flarg.l_type = F_UNLCK; + flarg.l_whence = SEEK_SET; + flarg.l_start = part->part_devoff; + flarg.l_len = PART2LOCKSIZE(part->part_devsize); + if (fcntl(fd, F_SETLK, &flarg) == -1) + warn("ukfs: cannot unlock device file"); +} + +/* + * Open the disk file and flock it. Also, if we are operation on + * an embedded partition, find the partition offset and size from + * the disklabel. + * + * We hard-fail only in two cases: + * 1) we failed to get the partition info out (don't know what offset + * to mount from) + * 2) we failed to flock the source device (i.e. fcntl() fails, + * not e.g. open() before it) + * + * Otherwise we let the code proceed to mount and let the file system + * throw the proper error. The only questionable bit is that if we + * soft-fail before flock and mount does succeed... + * + * Returns: -1 error (errno reports error code) + * 0 success + * + * dfdp: -1 device is not open + * n device is open + */ +static int +process_diskdevice(const char *devpath, struct ukfs_part *part, int rdonly, + int *dfdp) +{ + struct stat sb; + int rv = 0, devfd; + + /* defaults */ + *dfdp = -1; + + devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR); + if (devfd == -1) { + rv = errno; + goto out; + } + + if (fstat(devfd, &sb) == -1) { + rv = errno; + goto out; + } + + /* + * We do this only for non-block device since the + * (NetBSD) kernel allows block device open only once. + * We also need to close the device for fairly obvious reasons. + */ + if (!S_ISBLK(sb.st_mode)) { + struct flock flarg; + + memset(&flarg, 0, sizeof(flarg)); + flarg.l_type = rdonly ? F_RDLCK : F_WRLCK; + flarg.l_whence = SEEK_SET; + flarg.l_start = part->part_devoff; + flarg.l_len = PART2LOCKSIZE(part->part_devsize); + if (fcntl(devfd, F_SETLK, &flarg) == -1) { + pid_t holder; + int sverrno; + + sverrno = errno; + if (fcntl(devfd, F_GETLK, &flarg) != 1) + holder = flarg.l_pid; + else + holder = -1; + warnx("ukfs_mount: cannot lock device. held by pid %d", + holder); + rv = sverrno; + goto out; + } + } else { + close(devfd); + devfd = -1; + } + *dfdp = devfd; + + out: + if (rv) { + if (devfd != -1) + close(devfd); + } + + return rv; +} + +struct mountinfo { + const char *mi_vfsname; + const char *mi_mountpath; + int mi_mntflags; + void *mi_arg; + size_t mi_alen; + int *mi_error; +}; +static void * +mfs_mounter(void *arg) +{ + struct mountinfo *mi = arg; + int rv; + + rv = rump_sys_mount(mi->mi_vfsname, mi->mi_mountpath, mi->mi_mntflags, + mi->mi_arg, mi->mi_alen); + if (rv) { + warn("mfs mount failed. fix me."); + abort(); /* XXX */ + } + + return NULL; +} + +static struct ukfs * +doukfsmount(const char *vfsname, const char *devpath, struct ukfs_part *part, + const char *mountpath, int mntflags, void *arg, size_t alen) +{ + struct ukfs *fs = NULL; + struct lwp *curlwp; + int rv = 0, devfd = -1; + int mounted = 0; + int regged = 0; + + pthread_spin_lock(&part->part_lck); + part->part_refcount++; + pthread_spin_unlock(&part->part_lck); + if (part != ukfs_part_na) { + if ((rv = process_diskdevice(devpath, part, + mntflags & MNT_RDONLY, &devfd)) != 0) + goto out; + } + + fs = malloc(sizeof(struct ukfs)); + if (fs == NULL) { + rv = ENOMEM; + goto out; + } + memset(fs, 0, sizeof(struct ukfs)); + + /* create our mountpoint. this is never removed. */ + if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) { + if (errno != EEXIST) { + rv = errno; + goto out; + } + } + + if (part != ukfs_part_na) { + /* LINTED */ + rv = rump_pub_etfs_register_withsize(devpath, devpath, + RUMP_ETFS_BLK, part->part_devoff, part->part_devsize); + if (rv) { + goto out; + } + regged = 1; + } + + /* + * MFS is special since mount(2) doesn't return. Hence, we + * create a thread here. Could fix mfs to return, but there's + * too much history for me to bother. + */ + if (strcmp(vfsname, MOUNT_MFS) == 0) { +#if !defined(__minix) + pthread_t pt; +#endif + struct mountinfo mi; + int i; + + mi.mi_vfsname = vfsname; + mi.mi_mountpath = mountpath; + mi.mi_mntflags = mntflags; + mi.mi_arg = arg; + mi.mi_alen = alen; + +#if defined(__minix) + /* No support for threads. */ + rv = ENOTSUP; + goto out; +#else + if (pthread_create(&pt, NULL, mfs_mounter, &mi) == -1) { + rv = errno; + goto out; + } +#endif /* defined(__minix) */ + + for (i = 0;i < 100000; i++) { + struct statvfs svfsb; + + rv = rump_sys_statvfs1(mountpath, &svfsb, ST_WAIT); + if (rv == -1) { + rv = errno; + goto out; + } + + if (strcmp(svfsb.f_mntonname, mountpath) == 0 && + strcmp(svfsb.f_fstypename, MOUNT_MFS) == 0) { + break; + } + usleep(1); + } + } else { + rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen); + if (rv) { + rv = errno; + goto out; + } + } + + mounted = 1; + rv = rump_pub_vfs_getmp(mountpath, &fs->ukfs_mp); + if (rv) { + goto out; + } + + if (regged) { + fs->ukfs_devpath = strdup(devpath); + } + fs->ukfs_mountpath = strdup(mountpath); + pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED); + fs->ukfs_devfd = devfd; + fs->ukfs_part = part; + assert(rv == 0); + + curlwp = rump_pub_lwproc_curlwp(); + rump_pub_lwproc_newlwp(0); + fs->ukfs_lwp = rump_pub_lwproc_curlwp(); + fs->ukfs_cwd = strdup("/"); + rump_pub_lwproc_switch(curlwp); + + out: + if (rv) { + if (fs) { + free(fs); + fs = NULL; + } + if (mounted) + rump_sys_unmount(mountpath, MNT_FORCE); + if (regged) + rump_pub_etfs_remove(devpath); + if (devfd != -1) { + unlockdev(devfd, part); + close(devfd); + } + ukfs_part_release(part); + errno = rv; + } + + return fs; +} + +struct ukfs * +ukfs_mount(const char *vfsname, const char *devpath, + const char *mountpath, int mntflags, void *arg, size_t alen) +{ + + return doukfsmount(vfsname, devpath, ukfs_part_na, + mountpath, mntflags, arg, alen); +} + +struct ukfs * +ukfs_mount_disk(const char *vfsname, const char *devpath, + struct ukfs_part *part, const char *mountpath, int mntflags, + void *arg, size_t alen) +{ + + return doukfsmount(vfsname, devpath, part, + mountpath, mntflags, arg, alen); +} + +int +ukfs_release(struct ukfs *fs, int flags) +{ + struct lwp *curlwp = rump_pub_lwproc_curlwp(); + + /* get root lwp */ + rump_pub_lwproc_switch(fs->ukfs_lwp); + rump_pub_lwproc_rfork(RUMP_RFCFDG); + + if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) { + int rv, mntflag, error; + + mntflag = 0; + if (flags & UKFS_RELFLAG_FORCE) + mntflag = MNT_FORCE; + + rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag); + if (rv == -1) { + error = errno; + rump_pub_lwproc_releaselwp(); + if (curlwp) + rump_pub_lwproc_switch(curlwp); + errno = error; + return -1; + } + } + + if (fs->ukfs_devpath) { + rump_pub_etfs_remove(fs->ukfs_devpath); + free(fs->ukfs_devpath); + } + free(fs->ukfs_mountpath); + free(fs->ukfs_cwd); + + /* release this routine's lwp and ukfs base lwp */ + rump_pub_lwproc_releaselwp(); + rump_pub_lwproc_switch(fs->ukfs_lwp); + rump_pub_lwproc_releaselwp(); + + pthread_spin_destroy(&fs->ukfs_spin); + if (fs->ukfs_devfd != -1) { + unlockdev(fs->ukfs_devfd, fs->ukfs_part); + close(fs->ukfs_devfd); + } + ukfs_part_release(fs->ukfs_part); + free(fs); + + if (curlwp) + rump_pub_lwproc_switch(curlwp); + + return 0; +} + +void +ukfs_part_release(struct ukfs_part *part) +{ + int release; + + if (part != ukfs_part_none && part != ukfs_part_na) { + pthread_spin_lock(&part->part_lck); + release = --part->part_refcount == 0; + pthread_spin_unlock(&part->part_lck); + if (release) { + pthread_spin_destroy(&part->part_lck); + free(part); + } + } +} + +#define STDCALL(ukfs, thecall) \ + int rv = 0; \ + \ + PRECALL(); \ + rv = thecall; \ + POSTCALL(); \ + return rv; + +int +ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c) +{ + struct vnode *vp; + int rv; + + PRECALL(); + rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, + NULL, &vp, NULL); + POSTCALL(); + + if (rv == 0) { + RUMP_VOP_UNLOCK(vp); + } else { + errno = rv; + rv = -1; + } + + /*LINTED*/ + *c = (struct ukfs_dircookie *)vp; + return rv; +} + +static int +getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize) +{ + struct uio *uio; + size_t resid; + int rv, eofflag; + struct kauth_cred *cred; + + uio = rump_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ); + cred = rump_pub_cred_create(0, 0, 0, NULL); + rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL); + rump_pub_cred_put(cred); + RUMP_VOP_UNLOCK(vp); + *off = rump_pub_uio_getoff(uio); + resid = rump_pub_uio_free(uio); + + if (rv) { + errno = rv; + return -1; + } + + /* LINTED: not totally correct return type, but follows syscall */ + return bufsize - resid; +} + +/*ARGSUSED*/ +int +ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off, + uint8_t *buf, size_t bufsize) +{ + /*LINTED*/ + struct vnode *vp = (struct vnode *)c; + + RUMP_VOP_LOCK(vp, RUMP_LK_SHARED); + return getmydents(vp, off, buf, bufsize); +} + +int +ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off, + uint8_t *buf, size_t bufsize) +{ + struct vnode *vp; + int rv; + + PRECALL(); + rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, + NULL, &vp, NULL); + if (rv) { + POSTCALL(); + errno = rv; + return -1; + } + + rv = getmydents(vp, off, buf, bufsize); + rump_pub_vp_rele(vp); + POSTCALL(); + return rv; +} + +/*ARGSUSED*/ +int +ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c) +{ + + /*LINTED*/ + rump_pub_vp_rele((struct vnode *)c); + return 0; +} + +int +ukfs_open(struct ukfs *ukfs, const char *filename, int flags) +{ + int fd; + + PRECALL(); + fd = rump_sys_open(filename, flags, 0); + POSTCALL(); + if (fd == -1) + return -1; + + return fd; +} + +ssize_t +ukfs_read(struct ukfs *ukfs, const char *filename, off_t off, + uint8_t *buf, size_t bufsize) +{ + int fd; + ssize_t xfer = -1; /* XXXgcc */ + + PRECALL(); + fd = rump_sys_open(filename, RUMP_O_RDONLY, 0); + if (fd == -1) + goto out; + + xfer = rump_sys_pread(fd, buf, bufsize, off); + rump_sys_close(fd); + + out: + POSTCALL(); + if (fd == -1) { + return -1; + } + return xfer; +} + +/*ARGSUSED*/ +ssize_t +ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen) +{ + + return rump_sys_pread(fd, buf, buflen, off); +} + +ssize_t +ukfs_write(struct ukfs *ukfs, const char *filename, off_t off, + uint8_t *buf, size_t bufsize) +{ + int fd; + ssize_t xfer = -1; /* XXXgcc */ + + PRECALL(); + fd = rump_sys_open(filename, RUMP_O_WRONLY, 0); + if (fd == -1) + goto out; + + /* write and commit */ + xfer = rump_sys_pwrite(fd, buf, bufsize, off); + if (xfer > 0) + rump_sys_fsync(fd); + + rump_sys_close(fd); + + out: + POSTCALL(); + if (fd == -1) { + return -1; + } + return xfer; +} + +/*ARGSUSED*/ +ssize_t +ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen, + int dosync) +{ + ssize_t xfer; + + xfer = rump_sys_pwrite(fd, buf, buflen, off); + if (xfer > 0 && dosync) + rump_sys_fsync(fd); + + return xfer; +} + +/*ARGSUSED*/ +int +ukfs_close(struct ukfs *ukfs, int fd) +{ + + rump_sys_close(fd); + return 0; +} + +int +ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode) +{ + int fd; + + PRECALL(); + fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode); + if (fd == -1) + return -1; + rump_sys_close(fd); + + POSTCALL(); + return 0; +} + +int +ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev) +{ + + STDCALL(ukfs, rump_sys_mknod(path, mode, dev)); +} + +int +ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode) +{ + + STDCALL(ukfs, rump_sys_mkfifo(path, mode)); +} + +int +ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode) +{ + + STDCALL(ukfs, rump_sys_mkdir(filename, mode)); +} + +int +ukfs_remove(struct ukfs *ukfs, const char *filename) +{ + + STDCALL(ukfs, rump_sys_unlink(filename)); +} + +int +ukfs_rmdir(struct ukfs *ukfs, const char *filename) +{ + + STDCALL(ukfs, rump_sys_rmdir(filename)); +} + +int +ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create) +{ + + STDCALL(ukfs, rump_sys_link(filename, f_create)); +} + +int +ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname) +{ + + STDCALL(ukfs, rump_sys_symlink(filename, linkname)); +} + +ssize_t +ukfs_readlink(struct ukfs *ukfs, const char *filename, + char *linkbuf, size_t buflen) +{ + ssize_t rv; + + PRECALL(); + rv = rump_sys_readlink(filename, linkbuf, buflen); + POSTCALL(); + return rv; +} + +int +ukfs_rename(struct ukfs *ukfs, const char *from, const char *to) +{ + + STDCALL(ukfs, rump_sys_rename(from, to)); +} + +int +ukfs_chdir(struct ukfs *ukfs, const char *path) +{ + char *newpath, *oldpath; + int rv; + + PRECALL(); + rv = rump_sys_chdir(path); + if (rv == -1) + goto out; + + newpath = malloc(MAXPATHLEN); + if (rump_sys___getcwd(newpath, MAXPATHLEN) == -1) { + goto out; + } + + pthread_spin_lock(&ukfs->ukfs_spin); + oldpath = ukfs->ukfs_cwd; + ukfs->ukfs_cwd = newpath; + pthread_spin_unlock(&ukfs->ukfs_spin); + free(oldpath); + + out: + POSTCALL(); + return rv; +} + +int +ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) +{ + int rv; + + PRECALL(); + rv = rump_sys_stat(filename, file_stat); + POSTCALL(); + + return rv; +} + +int +ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) +{ + int rv; + + PRECALL(); + rv = rump_sys_lstat(filename, file_stat); + POSTCALL(); + + return rv; +} + +int +ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode) +{ + + STDCALL(ukfs, rump_sys_chmod(filename, mode)); +} + +int +ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode) +{ + + STDCALL(ukfs, rump_sys_lchmod(filename, mode)); +} + +int +ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) +{ + + STDCALL(ukfs, rump_sys_chown(filename, uid, gid)); +} + +int +ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) +{ + + STDCALL(ukfs, rump_sys_lchown(filename, uid, gid)); +} + +int +ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags) +{ + + STDCALL(ukfs, rump_sys_chflags(filename, flags)); +} + +int +ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags) +{ + + STDCALL(ukfs, rump_sys_lchflags(filename, flags)); +} + +int +ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr) +{ + + STDCALL(ukfs, rump_sys_utimes(filename, tptr)); +} + +int +ukfs_lutimes(struct ukfs *ukfs, const char *filename, + const struct timeval *tptr) +{ + + STDCALL(ukfs, rump_sys_lutimes(filename, tptr)); +} + +/* + * Dynamic module support + */ + +/* load one library */ + +/* + * XXX: the dlerror stuff isn't really threadsafe, but then again I + * can't protect against other threads calling dl*() outside of ukfs, + * so just live with it being flimsy + */ +int +ukfs_modload(const char *fname) +{ + void *handle; + const struct modinfo *const *mi_start, *const *mi_end; + int error; + + handle = dlopen(fname, RTLD_LAZY|RTLD_GLOBAL); + if (handle == NULL) { + const char *dlmsg = dlerror(); + if (strstr(dlmsg, "Undefined symbol")) + return 0; + warnx("dlopen %s failed: %s", fname, dlmsg); + /* XXXerrno */ + return -1; + } + + mi_start = dlsym(handle, "__start_link_set_modules"); + mi_end = dlsym(handle, "__stop_link_set_modules"); + if (mi_start && mi_end) { + error = rump_pub_module_init(mi_start, + (size_t)(mi_end-mi_start)); + if (error) + goto errclose; + return 1; + } + error = EINVAL; + + errclose: + dlclose(handle); + errno = error; + return -1; +} + +struct loadfail { + char *pname; + + LIST_ENTRY(loadfail) entries; +}; + +#define RUMPFSMOD_PREFIX "librumpfs_" +#define RUMPFSMOD_SUFFIX ".so" + +int +ukfs_modload_dir(const char *dir) +{ + char nbuf[MAXPATHLEN+1], *p; + struct dirent entry, *result; + DIR *libdir; + struct loadfail *lf, *nlf; + int error, nloaded = 0, redo; + LIST_HEAD(, loadfail) lfs; + + libdir = opendir(dir); + if (libdir == NULL) + return -1; + + LIST_INIT(&lfs); + for (;;) { + if ((error = readdir_r(libdir, &entry, &result)) != 0) + break; + if (!result) + break; + if (strncmp(result->d_name, RUMPFSMOD_PREFIX, + strlen(RUMPFSMOD_PREFIX)) != 0) + continue; + if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL) + || strlen(p) != strlen(RUMPFSMOD_SUFFIX)) + continue; + strlcpy(nbuf, dir, sizeof(nbuf)); + strlcat(nbuf, "/", sizeof(nbuf)); + strlcat(nbuf, result->d_name, sizeof(nbuf)); + switch (ukfs_modload(nbuf)) { + case 0: + lf = malloc(sizeof(*lf)); + if (lf == NULL) { + error = ENOMEM; + break; + } + lf->pname = strdup(nbuf); + if (lf->pname == NULL) { + free(lf); + error = ENOMEM; + break; + } + LIST_INSERT_HEAD(&lfs, lf, entries); + break; + case 1: + nloaded++; + break; + default: + /* ignore errors */ + break; + } + } + closedir(libdir); + if (error && nloaded != 0) + error = 0; + + /* + * El-cheapo dependency calculator. Just try to load the + * modules n times in a loop + */ + for (redo = 1; redo;) { + redo = 0; + nlf = LIST_FIRST(&lfs); + while ((lf = nlf) != NULL) { + nlf = LIST_NEXT(lf, entries); + if (ukfs_modload(lf->pname) == 1) { + nloaded++; + redo = 1; + LIST_REMOVE(lf, entries); + free(lf->pname); + free(lf); + } + } + } + + while ((lf = LIST_FIRST(&lfs)) != NULL) { + LIST_REMOVE(lf, entries); + free(lf->pname); + free(lf); + } + + if (error && nloaded == 0) { + errno = error; + return -1; + } + + return nloaded; +} + +/* XXX: this code uses definitions from NetBSD, needs rumpdefs */ +ssize_t +ukfs_vfstypes(char *buf, size_t buflen) +{ + int mib[3]; + struct sysctlnode q, ans[128]; + size_t alen; + int i; + + mib[0] = CTL_VFS; + mib[1] = VFS_GENERIC; + mib[2] = CTL_QUERY; + alen = sizeof(ans); + + memset(&q, 0, sizeof(q)); + q.sysctl_flags = SYSCTL_VERSION; + + if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) { + return -1; + } + + for (i = 0; i < alen/sizeof(ans[0]); i++) + if (strcmp("fstypes", ans[i].sysctl_name) == 0) + break; + if (i == alen/sizeof(ans[0])) { + errno = ENXIO; + return -1; + } + + mib[0] = CTL_VFS; + mib[1] = VFS_GENERIC; + mib[2] = ans[i].sysctl_num; + + if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) { + return -1; + } + + return buflen; +} + +/* + * Utilities + */ +static int +builddirs(const char *pathname, mode_t mode, + int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs) +{ + char *f1, *f2; + int rv; + mode_t mask; + bool end; + + /*ukfs_umask((mask = ukfs_umask(0)));*/ + umask((mask = umask(0))); + + f1 = f2 = strdup(pathname); + if (f1 == NULL) { + errno = ENOMEM; + return -1; + } + + end = false; + for (;;) { + /* find next component */ + f2 += strspn(f2, "/"); + f2 += strcspn(f2, "/"); + if (*f2 == '\0') + end = true; + else + *f2 = '\0'; + + rv = mkdirfn(fs, f1, mode & ~mask); + if (errno == EEXIST) + rv = 0; + + if (rv == -1 || *f2 != '\0' || end) + break; + + *f2 = '/'; + } + + free(f1); + + return rv; +} + +int +ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode) +{ + + return builddirs(pathname, mode, ukfs_mkdir, ukfs); +} diff --git a/lib/libukfs/ukfs.h b/lib/libukfs/ukfs.h new file mode 100644 index 000000000..8c5ec04c4 --- /dev/null +++ b/lib/libukfs/ukfs.h @@ -0,0 +1,153 @@ +/* $NetBSD: ukfs.h,v 1.14 2012/07/19 06:33:03 joerg Exp $ */ + +/* + * Copyright (c) 2007, 2008, 2009 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by the + * Finnish Cultural Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _RUMP_UKFS_H_ +#define _RUMP_UKFS_H_ + +#include + +#include + +/* don't include NetBSD for portability */ +struct vnode; +struct stat; +struct timeval; + +struct ukfs; +struct ukfs_dircookie; +struct ukfs_part; + +#define UKFS_DEFAULTMP "/ukfs" + +#define UKFS_RELFLAG_NOUNMOUNT 0x01 +#define UKFS_RELFLAG_FORCE 0x02 + +#define UKFS_VERSION 002 /* secret ukfs 002 */ +#define ukfs_init() _ukfs_init(UKFS_VERSION) + +__BEGIN_DECLS + +int _ukfs_init(int); +struct ukfs *ukfs_mount(const char *, const char *, const char *, + int, void *, size_t); +struct ukfs *ukfs_mount_disk(const char *, const char *, struct ukfs_part *, + const char *, int, void *, size_t); +int ukfs_release(struct ukfs *, int); + +int ukfs_opendir(struct ukfs *, const char *, + struct ukfs_dircookie **); +int ukfs_getdents(struct ukfs *, const char *, off_t *, + uint8_t *, size_t); +int ukfs_getdents_cookie(struct ukfs *, struct ukfs_dircookie *, + off_t *, uint8_t *, size_t); +int ukfs_closedir(struct ukfs *, struct ukfs_dircookie *); + +int ukfs_open(struct ukfs *, const char *, int); +ssize_t ukfs_read(struct ukfs *, const char *, off_t, + uint8_t *, size_t); +ssize_t ukfs_read_fd(struct ukfs *, int, off_t, uint8_t *, size_t); +ssize_t ukfs_write(struct ukfs *, const char *, off_t, + uint8_t *, size_t); +ssize_t ukfs_write_fd(struct ukfs *, int, off_t, uint8_t *, size_t,int); +int ukfs_close(struct ukfs *, int); + +ssize_t ukfs_readlink(struct ukfs *, const char *, char *, size_t); + +int ukfs_create(struct ukfs *, const char *, mode_t); +int ukfs_mkdir(struct ukfs *, const char *, mode_t); +int ukfs_mknod(struct ukfs *, const char *, mode_t, dev_t); +int ukfs_mkfifo(struct ukfs *, const char *, mode_t); +int ukfs_symlink(struct ukfs *, const char *, const char *); + +int ukfs_remove(struct ukfs *, const char *); +int ukfs_rmdir(struct ukfs *, const char *); + +int ukfs_link(struct ukfs *, const char *, const char *); +int ukfs_rename(struct ukfs *, const char *, const char *); + +int ukfs_chdir(struct ukfs *, const char *); + +int ukfs_stat(struct ukfs *, const char *, struct stat *); +int ukfs_lstat(struct ukfs *, const char *, struct stat *); + +int ukfs_chmod(struct ukfs *, const char *, mode_t); +int ukfs_lchmod(struct ukfs *, const char *, mode_t); +int ukfs_chown(struct ukfs *, const char *, uid_t, gid_t); +int ukfs_lchown(struct ukfs *, const char *, uid_t, gid_t); +int ukfs_chflags(struct ukfs *, const char *, u_long); +int ukfs_lchflags(struct ukfs *, const char *, u_long); + +int ukfs_utimes(struct ukfs *, const char *, + const struct timeval *); +int ukfs_lutimes(struct ukfs *, const char *, + const struct timeval *); + +struct mount *ukfs_getmp(struct ukfs *); +struct vnode *ukfs_getrvp(struct ukfs *); +void ukfs_setspecific(struct ukfs *, void *); +void * ukfs_getspecific(struct ukfs *); + +/* partition magic in device names */ +extern struct ukfs_part *ukfs_part_none; +extern struct ukfs_part *ukfs_part_na; +#define UKFS_PARTITION_SCANMAGIC "%PART:" /* deprecated */ + +#define UKFS_DISKLABEL_SCANMAGIC "%DISKLABEL:" +#define UKFS_DISKLABEL_MAGICLEN (sizeof(UKFS_DISKLABEL_SCANMAGIC "a%")-1) + +#define UKFS_OFFSET_SCANMAGIC "%OFFSET:" +#define UKFS_OFFSET_MINLEN (sizeof(UKFS_OFFSET_SCANMAGIC "512,512%")-1) + +#define UKFS_DEVICE_MAXSTR 128 /* unexact science ... */ +#define UKFS_DEVICE_MAXPATHLEN (MAXPATHLEN+UKFS_DEVICE_MAXSTR) + +#define UKFS_DEVICE_ARGVPROBE(part) \ +do { \ + if (argc < 3) \ + *part = NULL; \ + else if (ukfs_part_probe(argv[argc-2], part) == -1) \ + err(1, "ukfs_part_probe"); \ +} while (/*CONSTCOND*/0) + +int ukfs_part_probe(char *, struct ukfs_part **); +void ukfs_part_release(struct ukfs_part *); +int ukfs_part_tostring(struct ukfs_part *, char *, size_t); + +/* dynamic loading of library modules */ +int ukfs_modload(const char *); +int ukfs_modload_dir(const char *); +ssize_t ukfs_vfstypes(char *, size_t); + +/* Utilities */ +int ukfs_util_builddirs(struct ukfs *, const char *, mode_t); + +__END_DECLS + +#endif /* _RUMP_UKFS_H_ */ diff --git a/lib/libukfs/ukfs_disklabel.c b/lib/libukfs/ukfs_disklabel.c new file mode 100644 index 000000000..ff7e0d7dd --- /dev/null +++ b/lib/libukfs/ukfs_disklabel.c @@ -0,0 +1,150 @@ +/* $NetBSD: ukfs_disklabel.c,v 1.3 2011/02/22 15:42:15 pooka Exp $ */ + +/* + * Local copies of libutil disklabel routines. This uncouples libukfs + * from the NetBSD-only libutil. All identifiers are prefixed with + * ukfs or UKFS, otherwise the routines are the same. + */ + +/* + * From: + * NetBSD: disklabel_scan.c,v 1.3 2009/01/18 12:13:03 lukem Exp + */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roland C. Dowdeswell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "ukfs_int_disklabel.h" + +#define SCAN_INCR 4 + +int +ukfs__disklabel_scan(struct ukfs__disklabel *lp, int *isswapped, + char *buf, size_t buflen) +{ + size_t i; + int imswapped; + uint16_t npart; + + /* scan for the correct magic numbers. */ + + for (i=0; i <= buflen - sizeof(*lp); i += SCAN_INCR) { + memcpy(lp, buf + i, sizeof(*lp)); + if (lp->d_magic == UKFS_DISKMAGIC && + lp->d_magic2 == UKFS_DISKMAGIC) { + imswapped = 0; + goto sanity; + } + if (lp->d_magic == bswap32(UKFS_DISKMAGIC) && + lp->d_magic2 == bswap32(UKFS_DISKMAGIC)) { + imswapped = 1; + goto sanity; + } + } + + return 1; + +sanity: + if (imswapped) + npart = bswap16(lp->d_npartitions); + else + npart = lp->d_npartitions; + /* we've found something, let's sanity check it */ + if (npart > UKFS_MAXPARTITIONS + || ukfs__disklabel_dkcksum(lp, imswapped)) + return 1; + + *isswapped = imswapped; + return 0; +} + + +/* + * From: + * $NetBSD: disklabel_dkcksum.c,v 1.4 2005/05/15 21:01:34 thorpej Exp + */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +uint16_t +ukfs__disklabel_dkcksum(struct ukfs__disklabel *lp, int imswapped) +{ + uint16_t *start, *end; + uint16_t sum; + uint16_t npart; + + if (imswapped) + npart = bswap16(lp->d_npartitions); + else + npart = lp->d_npartitions; + + sum = 0; + start = (uint16_t *)(void *)lp; + end = (uint16_t *)(void *)&lp->d_partitions[npart]; + while (start < end) { + if (imswapped) + sum ^= bswap16(*start); + else + sum ^= *start; + start++; + } + return (sum); +} diff --git a/lib/libukfs/ukfs_int_disklabel.h b/lib/libukfs/ukfs_int_disklabel.h new file mode 100644 index 000000000..304f220ea --- /dev/null +++ b/lib/libukfs/ukfs_int_disklabel.h @@ -0,0 +1,158 @@ +/* $NetBSD: ukfs_int_disklabel.h,v 1.3 2011/02/22 15:42:15 pooka Exp $ */ + +/* + * Modified copy of disklabel.h so that ukfs doesn't have to depend + * on NetBSD headers. Contains just about only "struct disklabel". + * The on-disk disklabel doesn't really change format, so no need + * to track this against NetBSD changes. + */ + +/* + * Copyright (c) 1987, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)disklabel.h 8.2 (Berkeley) 7/10/94 + */ + +#ifndef LIB_UKFS_DISKLABEL_H_ +#define LIB_UKFS_DISKLABEL_H_ + +#include + +#include + +#define UKFS_MAXPARTITIONS 22 +#define UKFS_DISKMAGIC ((uint32_t)0x82564557) /* The disk magic number */ + +struct ukfs__disklabel { + uint32_t d_magic; /* the magic number */ + uint16_t d_type; /* drive type */ + uint16_t d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + + /* + * d_packname contains the pack identifier and is returned when + * the disklabel is read off the disk or in-core copy. + * d_boot0 and d_boot1 are the (optional) names of the + * primary (block 0) and secondary (block 1-15) bootstraps + * as found in /usr/mdec. These are returned when using + * getdiskbyname(3) to retrieve the values from /etc/disktab. + */ + union { + char un_d_packname[16]; /* pack identifier */ + struct { + char *un_d_boot0; /* primary bootstrap name */ + char *un_d_boot1; /* secondary bootstrap name */ + } un_b; + } d_un; +#define d_packname d_un.un_d_packname +#define d_boot0 d_un.un_b.un_d_boot0 +#define d_boot1 d_un.un_b.un_d_boot1 + + /* disk geometry: */ + uint32_t d_secsize; /* # of bytes per sector */ + uint32_t d_nsectors; /* # of data sectors per track */ + uint32_t d_ntracks; /* # of tracks per cylinder */ + uint32_t d_ncylinders; /* # of data cylinders per unit */ + uint32_t d_secpercyl; /* # of data sectors per cylinder */ + uint32_t d_secperunit; /* # of data sectors per unit */ + + /* + * Spares (bad sector replacements) below are not counted in + * d_nsectors or d_secpercyl. Spare sectors are assumed to + * be physical sectors which occupy space at the end of each + * track and/or cylinder. + */ + uint16_t d_sparespertrack; /* # of spare sectors per track */ + uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternative cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + uint32_t d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the + * formatter or controller when formatting. When interleaving is + * in use, logically adjacent sectors are not physically + * contiguous, but instead are separated by some number of + * sectors. It is specified as the ratio of physical sectors + * traversed per logical sector. Thus an interleave of 1:1 + * implies contiguous layout, while 2:1 implies that logical + * sector 0 is separated by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N relative to + * sector 0 on track N-1 on the same cylinder. Finally, d_cylskew + * is the offset of sector 0 on cylinder N relative to sector 0 + * on cylinder N-1. + */ + uint16_t d_rpm; /* rotational speed */ + uint16_t d_interleave; /* hardware sector interleave */ + uint16_t d_trackskew; /* sector 0 skew, per track */ + uint16_t d_cylskew; /* sector 0 skew, per cylinder */ + uint32_t d_headswitch; /* head switch time, usec */ + uint32_t d_trkseek; /* track-to-track seek, usec */ + uint32_t d_flags; /* generic flags */ +#define NDDATA 5 + uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + uint32_t d_spare[NSPARE]; /* reserved for future use */ + uint32_t d_magic2; /* the magic number (again) */ + uint16_t d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + uint16_t d_npartitions; /* number of partitions in following */ + uint32_t d_bbsize; /* size of boot area at sn0, bytes */ + uint32_t d_sbsize; /* max size of fs superblock, bytes */ + struct ukfs__partition { /* the partition table */ + uint32_t p_size; /* number of sectors in partition */ + uint32_t p_offset; /* starting sector */ + union { + uint32_t fsize; /* FFS, ADOS: + filesystem basic fragment size */ + uint32_t cdsession; /* ISO9660: session offset */ + } __partition_u2; +#define p_fsize __partition_u2.fsize +#define p_cdsession __partition_u2.cdsession + uint8_t p_fstype; /* filesystem type, see below */ + uint8_t p_frag; /* filesystem fragments per block */ + union { + uint16_t cpg; /* UFS: FS cylinders per group */ + uint16_t sgs; /* LFS: FS segment shift */ + } __partition_u1; +#define p_cpg __partition_u1.cpg +#define p_sgs __partition_u1.sgs + } d_partitions[UKFS_MAXPARTITIONS]; /* actually may be more */ +}; + +uint16_t ukfs__disklabel_dkcksum(struct ukfs__disklabel *, int); +int ukfs__disklabel_scan(struct ukfs__disklabel *, int *, + char *, size_t); + +#endif /* !LIB_UKFS_DISKLABEL_H_ */