Many, many thanks to Masanori OZAWA <ozawa@ongs.co.jp>
and Daichi GOTO <daichi@FreeBSD.org> for submitting this major rewrite of unionfs. This rewrite was done to try to solve many of the longstanding crashing and locking issues in the existing unionfs implementation. This implementation also adds a 'MASQUERADE mode', which allows the user to set different user, group, and file permission modes in the upper layer. Submitted by: daichi, Masanori OZAWA Reviewed by: rodrigc (modified for minor style issues)
This commit is contained in:
parent
113db579f0
commit
c4618bacd3
@ -7,7 +7,7 @@ MAN= mount_unionfs.8
|
||||
|
||||
MOUNT= ${.CURDIR}/../mount
|
||||
CFLAGS+=-I${MOUNT}
|
||||
WARNS?= 0
|
||||
WARNS?= 3
|
||||
|
||||
.PATH: ${MOUNT}
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 27, 1994
|
||||
.Dd November 30, 2006
|
||||
.Dt MOUNT_UNIONFS 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -64,6 +64,17 @@ layer.
|
||||
The options are as follows:
|
||||
.Bl -tag -width indent
|
||||
.It Fl b
|
||||
Deprecated. Use
|
||||
.Fl o
|
||||
.Ar below
|
||||
instead.
|
||||
.It Fl o
|
||||
Options are specified with a
|
||||
.Fl o
|
||||
flag followed by an option.
|
||||
The following options are available:
|
||||
.Bl -tag -width indent
|
||||
.It Cm below
|
||||
Invert the default position, so that
|
||||
.Ar directory
|
||||
becomes the lower layer and
|
||||
@ -72,16 +83,36 @@ becomes the upper layer.
|
||||
However,
|
||||
.Ar uniondir
|
||||
remains the mount point.
|
||||
.It Fl o
|
||||
Options are specified with a
|
||||
.Fl o
|
||||
flag followed by a comma separated string of options.
|
||||
See the
|
||||
.Xr mount 8
|
||||
man page for possible options and their meanings.
|
||||
.It Fl r
|
||||
Hide the lower layer completely in the same way as mounting with
|
||||
.Xr mount_nullfs 8 .
|
||||
.It Cm copymode=traditional | transparent | masquerade
|
||||
Specifies the way to create a file or a directory in the upper layer
|
||||
automatically when needed.
|
||||
.Ar traditional
|
||||
uses the same way as the old unionfs for backward compatibility, and
|
||||
.Ar transparent
|
||||
duplicates the file and directory mode bits and the ownership in the
|
||||
lower layer to the created file in the upper layer.
|
||||
For behavior of the
|
||||
.Ar masquerade
|
||||
mode, see
|
||||
.Sx MASQUERADE MODE .
|
||||
.It Cm udir=mode
|
||||
Specifies directory mode bits in octal for
|
||||
.Ar masquerade
|
||||
mode.
|
||||
.It Cm ufile=mode
|
||||
Specifies file mode bits in octal for
|
||||
.Ar masquerade
|
||||
mode.
|
||||
.It Cm gid=gid
|
||||
Specifies group for
|
||||
.Ar masquerade
|
||||
mode.
|
||||
.It Cm uid=uid
|
||||
.uid
|
||||
Specifies user for
|
||||
.Ar masquerade
|
||||
mode.
|
||||
.El
|
||||
.El
|
||||
.Pp
|
||||
To enforce file system security, the user mounting the file system
|
||||
@ -91,6 +122,13 @@ In addition, the
|
||||
.Va vfs.usermount
|
||||
.Xr sysctl 8
|
||||
variable must be set to 1 to permit file system mounting by ordinary users.
|
||||
However, note that
|
||||
.Ar transparent
|
||||
and
|
||||
.Ar masquerade
|
||||
mode require
|
||||
.Va vfs.usermount
|
||||
be set to 0 because this functionality can only be used by superusers.
|
||||
.Pp
|
||||
Filenames are looked up in the upper layer and then in the
|
||||
lower layer.
|
||||
@ -98,10 +136,14 @@ If a directory is found in the lower layer, and there is no entry
|
||||
in the upper layer, then a
|
||||
.Em shadow
|
||||
directory will be created in the upper layer.
|
||||
It will be owned by the user who originally did the union mount,
|
||||
with mode
|
||||
.Dq rwxrwxrwx
|
||||
(0777) modified by the umask in effect at that time.
|
||||
The ownership and the mode bits are set depending on the
|
||||
.Ar copymode
|
||||
option. In
|
||||
.Ar traditional
|
||||
mode, it will be owned by the user who originally did the
|
||||
union mount, with mode 0777
|
||||
.Dq rwxrwxrwx
|
||||
modified by the umask in effect at that time.
|
||||
.Pp
|
||||
If a file exists in the upper layer then there is no way to access
|
||||
a file with the same name in the lower layer.
|
||||
@ -142,15 +184,74 @@ option to
|
||||
.Xr mount 8
|
||||
which only applies the union operation to the mount point itself,
|
||||
and then only for lookups.
|
||||
.Sh MASQUERADE MODE
|
||||
When a file
|
||||
.Pq or a directory
|
||||
is created in the upper layer, the
|
||||
.Ar masquerade
|
||||
mode sets it the fixed access mode bits given in
|
||||
.Ar ufile Pq for files
|
||||
or
|
||||
.Ar udir Pq for directories
|
||||
option and the owner given in
|
||||
.Ar udir
|
||||
and
|
||||
.Ar gid
|
||||
options, instead of ones in the lower layer. Note that in the
|
||||
.Ar masquerade
|
||||
mode and when owner of the file or directory matches
|
||||
one specified in
|
||||
.Ar uid
|
||||
option, only mode bits for the owner will be modified.
|
||||
More specifically, the file mode bits in the upper layer will
|
||||
be
|
||||
.Pq mode in the lower layer
|
||||
OR
|
||||
.Pq Po mode given in .Ar ufile
|
||||
AND 0700
|
||||
.Pc , and the ownership will be the same as one in the lower layer.
|
||||
.Pp
|
||||
The default values for
|
||||
.Ar ufile , udir , uid ,
|
||||
and
|
||||
.Ar gid
|
||||
are as follow:
|
||||
.Pp
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
If both
|
||||
.Ar ufile
|
||||
and
|
||||
.Ar udir
|
||||
are not specified, access mode bits in the mount point will be used.
|
||||
.It
|
||||
If both
|
||||
.Ar uid
|
||||
and
|
||||
.Ar gid
|
||||
are not specified, ownership in the mount point will be used.
|
||||
.It
|
||||
If either
|
||||
.Ar udir
|
||||
or
|
||||
.Ar ufile
|
||||
is not specified, the other will be the same as the specified one.
|
||||
.It
|
||||
If either
|
||||
.Ar uid
|
||||
or
|
||||
.Ar gid
|
||||
is not specified, the other will be the same as the specified one.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
The commands
|
||||
.Bd -literal -offset indent
|
||||
mount -t cd9660 -o ro /dev/cd0a /usr/src
|
||||
mount -t unionfs /var/obj /usr/src
|
||||
mount -t cd9660 -o ro /dev/cd0 /usr/src
|
||||
mount -t unionfs -o noatime /var/obj /usr/src
|
||||
.Ed
|
||||
.Pp
|
||||
mount the CD-ROM drive
|
||||
.Pa /dev/cd0a
|
||||
.Pa /dev/cd0
|
||||
on
|
||||
.Pa /usr/src
|
||||
and then attaches
|
||||
@ -158,11 +259,42 @@ and then attaches
|
||||
on top.
|
||||
For most purposes the effect of this is to make the
|
||||
source tree appear writable
|
||||
even though it is stored on a CD-ROM.
|
||||
even though it is stored on a CD-ROM. The
|
||||
.Fl o Ar noatime
|
||||
option is useful to avoid unnecessary copying from the lower to the
|
||||
upper layer.
|
||||
.Pp
|
||||
The commands
|
||||
.Bd -literal -offset indent
|
||||
mount -t cd9660 -o ro /dev/cd0 /usr/src
|
||||
chown 2020 /usr/src
|
||||
mount -t unionfs -o noatime -o copymode=masquerade -o uid=builder \\
|
||||
-o udir=755 -o ufile=644 /var/obj /usr/src
|
||||
.Ed
|
||||
.Pp
|
||||
also mount the CD-ROM drive
|
||||
.Pa /dev/cd0
|
||||
on
|
||||
.Pa /usr/src
|
||||
and then attaches
|
||||
.Pa /var/obj
|
||||
on top. Furthermore, the owner of all files and directories in /usr/src
|
||||
is a regular user with uid
|
||||
.Pq 2020
|
||||
when seen from the upper layer. Note that for the access mode bits,
|
||||
ones in the lower layer
|
||||
.Pq on the CD-ROM, in this example
|
||||
are still used without change.
|
||||
Thus, write privilege to the upper layer can be controlled
|
||||
independently from access mode bits and ownership in the lower layer.
|
||||
If a user does not have read privilege from the lower layer,
|
||||
one cannot still read even when the upper layer is mounted by using
|
||||
.Ar masquerade
|
||||
mode.
|
||||
.Pp
|
||||
The command
|
||||
.Bd -literal -offset indent
|
||||
mount -t unionfs -o -b /sys $HOME/sys
|
||||
mount -t unionfs -o noatime -o below /sys $HOME/sys
|
||||
.Ed
|
||||
.Pp
|
||||
attaches the system source tree below the
|
||||
@ -186,8 +318,20 @@ The
|
||||
.Nm
|
||||
utility first appeared in
|
||||
.Bx 4.4 .
|
||||
It first worked in
|
||||
.Fx Ns -(fill this in) .
|
||||
.Pp
|
||||
The
|
||||
.Fl r
|
||||
option for hiding the lower layer completely was removed in
|
||||
.Fx 7.0
|
||||
because this is identical to using
|
||||
.Xr mount_nullfs 8 .
|
||||
.Sh AUTHORS
|
||||
In
|
||||
.Fx 7.0 ,
|
||||
.An Masanori OZAWA Aq ozawa@ongs.co.jp
|
||||
reimplemented handling of locking, whiteout, and file mode bits, and
|
||||
.An Hiroki Sato Aq hrs@FreeBSD.org
|
||||
wrote about the changes in this manual page.
|
||||
.Sh BUGS
|
||||
THIS FILE SYSTEM TYPE IS NOT YET FULLY SUPPORTED (READ: IT DOESN'T WORK)
|
||||
AND USING IT MAY, IN FACT, DESTROY DATA ON YOUR SYSTEM.
|
||||
@ -198,7 +342,7 @@ SLIPPERY WHEN WET.
|
||||
.Pp
|
||||
This code also needs an owner in order to be less dangerous - serious
|
||||
hackers can apply by sending mail to
|
||||
.Aq hackers@FreeBSD.org
|
||||
.Aq freebsd-fs@FreeBSD.org
|
||||
and announcing
|
||||
their intent to take it over.
|
||||
.Pp
|
||||
@ -214,3 +358,20 @@ Running
|
||||
.Xr find 1
|
||||
over a union tree has the side-effect of creating
|
||||
a tree of shadow directories in the upper layer.
|
||||
.Pp
|
||||
The current implementation does not support copying extended attributes
|
||||
for
|
||||
.Xr acl 9 ,
|
||||
.Xr mac 9 ,
|
||||
or so on to the upper layer. Note that this may be a security issue.
|
||||
.Pp
|
||||
A shadow directory, which is one automatically created in the upper
|
||||
layer when it exists in the lower layer and does not exist in the
|
||||
upper layer, is always created with the superuser privilege.
|
||||
However, a file copied from the lower layer in the same way
|
||||
is created by the user who accessed it. Because of this,
|
||||
if the user is not the superuser, even in
|
||||
.Ar transparent
|
||||
mode the access mode bits in the copied file in the upper layer
|
||||
will not always be the same as ones in the lower layer.
|
||||
This behavior should be fixed.
|
||||
|
@ -1,6 +1,9 @@
|
||||
/*
|
||||
/*-
|
||||
* Copyright (c) 1992, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* The Regents of the University of California.
|
||||
* Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
|
||||
* Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software donated to Berkeley by
|
||||
* Jan-Simon Pendry.
|
||||
@ -48,6 +51,7 @@ static const char rcsid[] =
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
@ -55,54 +59,115 @@ static const char rcsid[] =
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "mntopts.h"
|
||||
|
||||
static struct mntopt mopts[] = {
|
||||
MOPT_STDOPTS,
|
||||
MOPT_END
|
||||
};
|
||||
|
||||
static int subdir(const char *, const char *);
|
||||
static void usage (void) __dead2;
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
static int
|
||||
subdir(const char *p, const char *dir)
|
||||
{
|
||||
struct iovec iov[8];
|
||||
int ch, mntflags;
|
||||
char source[MAXPATHLEN];
|
||||
char target[MAXPATHLEN];
|
||||
int iovcnt;
|
||||
int l;
|
||||
|
||||
iovcnt = 6;
|
||||
l = strlen(dir);
|
||||
if (l <= 1)
|
||||
return (1);
|
||||
|
||||
if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"usage: mount_unionfs [-o options] directory uniondir\n");
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_gid(const char *s, char *buf, size_t bufsize)
|
||||
{
|
||||
struct group *gr;
|
||||
char *inval;
|
||||
|
||||
if ((gr = getgrnam(s)) != NULL)
|
||||
snprintf(buf, bufsize, "%d", gr->gr_gid);
|
||||
else {
|
||||
strtol(s, &inval, 10);
|
||||
if (*inval != 0) {
|
||||
errx(EX_NOUSER, "unknown group id: %s", s);
|
||||
usage();
|
||||
} else {
|
||||
strncpy(buf, s, bufsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uid_t
|
||||
parse_uid(const char *s, char *buf, size_t bufsize)
|
||||
{
|
||||
struct passwd *pw;
|
||||
char *inval;
|
||||
|
||||
if ((pw = getpwnam(s)) != NULL)
|
||||
snprintf(buf, bufsize, "%d", pw->pw_uid);
|
||||
else {
|
||||
strtol(s, &inval, 10);
|
||||
if (*inval != 0) {
|
||||
errx(EX_NOUSER, "unknown user id: %s", s);
|
||||
usage();
|
||||
} else {
|
||||
strncpy(buf, s, bufsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct iovec *iov;
|
||||
int ch, mntflags, iovlen;
|
||||
char source [MAXPATHLEN], target[MAXPATHLEN], errmsg[255];
|
||||
char uid_str[20], gid_str[20];
|
||||
char *p, *val;
|
||||
|
||||
iov = NULL;
|
||||
iovlen = 0;
|
||||
mntflags = 0;
|
||||
while ((ch = getopt(argc, argv, "bo:r")) != -1)
|
||||
memset(errmsg, 0, sizeof(errmsg));
|
||||
|
||||
while ((ch = getopt(argc, argv, "bo:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
iov[6].iov_base = "below";
|
||||
iov[6].iov_len = strlen(iov[6].iov_base) + 1;
|
||||
iov[7].iov_base = NULL;
|
||||
iov[7].iov_len = 0;
|
||||
iovcnt = 8;
|
||||
printf("\n -b is deprecated. Use \"-o below\" instead\n");
|
||||
build_iovec(&iov, &iovlen, "below", NULL, 0);
|
||||
break;
|
||||
case 'o':
|
||||
getmntopts(optarg, mopts, &mntflags, 0);
|
||||
break;
|
||||
case 'r':
|
||||
iov[6].iov_base = "replace";
|
||||
iov[6].iov_len = strlen(iov[6].iov_base) + 1;
|
||||
iov[7].iov_base = NULL;
|
||||
iov[7].iov_len = 0;
|
||||
iovcnt = 8;
|
||||
p = strchr(optarg, '=');
|
||||
val = NULL;
|
||||
if (p != NULL) {
|
||||
*p = '\0';
|
||||
val = p + 1;
|
||||
if (strncmp(optarg, "gid", 3) == 0) {
|
||||
parse_gid(val, gid_str, sizeof(gid_str));
|
||||
val = gid_str;
|
||||
}
|
||||
else if (strncmp(optarg, "uid", 3) == 0) {
|
||||
parse_uid(val, uid_str, sizeof(uid_str));
|
||||
val = uid_str;
|
||||
}
|
||||
}
|
||||
build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
@ -115,46 +180,14 @@ main(argc, argv)
|
||||
|
||||
if (subdir(target, source) || subdir(source, target))
|
||||
errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths",
|
||||
argv[0], target, argv[1], source);
|
||||
argv[0], target, argv[1], source);
|
||||
|
||||
iov[0].iov_base = "fstype";
|
||||
iov[0].iov_len = strlen(iov[0].iov_base) + 1;
|
||||
iov[1].iov_base = "unionfs";
|
||||
iov[1].iov_len = strlen(iov[1].iov_base) + 1;
|
||||
iov[2].iov_base = "fspath";
|
||||
iov[2].iov_len = strlen(iov[2].iov_base) + 1;
|
||||
iov[3].iov_base = source;
|
||||
iov[3].iov_len = strlen(source) + 1;
|
||||
iov[4].iov_base = "target";
|
||||
iov[4].iov_len = strlen(iov[4].iov_base) + 1;
|
||||
iov[5].iov_base = target;
|
||||
iov[5].iov_len = strlen(target) + 1;
|
||||
if (nmount(iov, iovcnt, mntflags))
|
||||
err(EX_OSERR, "%s", target);
|
||||
build_iovec(&iov, &iovlen, "fstype", "unionfs", (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "fspath", source, (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "from", target, (size_t)-1);
|
||||
build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
|
||||
|
||||
if (nmount(iov, iovlen, mntflags))
|
||||
err(EX_OSERR, "%s: %s", source, errmsg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
subdir(p, dir)
|
||||
const char *p;
|
||||
const char *dir;
|
||||
{
|
||||
int l;
|
||||
|
||||
l = strlen(dir);
|
||||
if (l <= 1)
|
||||
return (1);
|
||||
|
||||
if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0'))
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"usage: mount_unionfs [-br] [-o options] directory uniondir\n");
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*-
|
||||
* Copyright (c) 1994 The Regents of the University of California.
|
||||
* Copyright (c) 1994 Jan-Simon Pendry.
|
||||
* Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
|
||||
* Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software donated to Berkeley by
|
||||
@ -34,107 +36,100 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#define UNMNT_ABOVE 0x0001 /* Target appears above mount point */
|
||||
#define UNMNT_BELOW 0x0002 /* Target appears below mount point */
|
||||
#define UNMNT_REPLACE 0x0003 /* Target replaces mount point */
|
||||
|
||||
struct union_mount {
|
||||
struct vnode *um_uppervp; /* UN_ULOCK holds locking state */
|
||||
struct vnode *um_lowervp; /* Left unlocked */
|
||||
struct ucred *um_cred; /* Credentials of user calling mount */
|
||||
int um_cmode; /* cmask from mount process */
|
||||
int um_op; /* Operation mode */
|
||||
dev_t um_upperdev; /* Upper root node fsid[0]*/
|
||||
};
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#ifndef DIAGNOSTIC
|
||||
#define DIAGNOSTIC
|
||||
#endif
|
||||
/* copy method of attr from lower to upper */
|
||||
typedef enum _unionfs_copymode {
|
||||
UNIONFS_TRADITIONAL = 0,
|
||||
UNIONFS_TRANSPARENT,
|
||||
UNIONFS_MASQUERADE
|
||||
} unionfs_copymode;
|
||||
|
||||
/*
|
||||
* DEFDIRMODE is the mode bits used to create a shadow directory.
|
||||
*/
|
||||
#define VRWXMODE (VREAD|VWRITE|VEXEC)
|
||||
#define VRWMODE (VREAD|VWRITE)
|
||||
#define UN_DIRMODE ((VRWXMODE)|(VRWXMODE>>3)|(VRWXMODE>>6))
|
||||
#define UN_FILEMODE ((VRWMODE)|(VRWMODE>>3)|(VRWMODE>>6))
|
||||
|
||||
/*
|
||||
* A cache of vnode references (hangs off v_data)
|
||||
*/
|
||||
struct union_node {
|
||||
LIST_ENTRY(union_node) un_cache; /* Hash chain */
|
||||
struct vnode *un_vnode; /* Back pointer */
|
||||
struct vnode *un_uppervp; /* overlaying object */
|
||||
struct vnode *un_lowervp; /* underlying object */
|
||||
struct vnode *un_dirvp; /* Parent dir of uppervp */
|
||||
struct vnode *un_pvp; /* Parent vnode */
|
||||
char *un_path; /* saved component name */
|
||||
int un_openl; /* # of opens on lowervp */
|
||||
int un_exclcnt; /* exclusive count */
|
||||
unsigned int un_flags;
|
||||
struct vnode **un_dircache; /* cached union stack */
|
||||
off_t un_uppersz; /* size of upper object */
|
||||
off_t un_lowersz; /* size of lower object */
|
||||
#ifdef DIAGNOSTIC
|
||||
pid_t un_pid;
|
||||
#endif
|
||||
struct unionfs_mount {
|
||||
struct vnode *um_lowervp; /* VREFed once */
|
||||
struct vnode *um_uppervp; /* VREFed once */
|
||||
struct vnode *um_rootvp; /* ROOT vnode */
|
||||
unionfs_copymode um_copymode;
|
||||
uid_t um_uid;
|
||||
gid_t um_gid;
|
||||
u_short um_udir;
|
||||
u_short um_ufile;
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX UN_ULOCK - indicates that the uppervp is locked
|
||||
*
|
||||
* UN_CACHED - node is in the union cache
|
||||
*/
|
||||
/* unionfs status list */
|
||||
struct unionfs_node_status {
|
||||
LIST_ENTRY(unionfs_node_status) uns_list; /* Status list */
|
||||
lwpid_t uns_tid; /* current thread id */
|
||||
int uns_node_flag; /* uns flag */
|
||||
int uns_lower_opencnt; /* open count of lower */
|
||||
int uns_upper_opencnt; /* open count of upper */
|
||||
int uns_lower_openmode; /* open mode of lower */
|
||||
int uns_lower_fdidx; /* open fdidx of lower */
|
||||
int uns_readdir_status; /* read status of readdir */
|
||||
};
|
||||
|
||||
/*#define UN_ULOCK 0x04*/ /* Upper node is locked */
|
||||
#define UN_CACHED 0x10 /* In union cache */
|
||||
/* union node status flags */
|
||||
#define UNS_OPENL_4_READDIR 0x01 /* open lower layer for readdir */
|
||||
|
||||
/*
|
||||
* Hash table locking flags
|
||||
*/
|
||||
/* A cache of vnode references */
|
||||
struct unionfs_node {
|
||||
LIST_ENTRY(unionfs_node) un_hash; /* Hash list */
|
||||
struct vnode *un_lowervp; /* lower side vnode */
|
||||
struct vnode *un_uppervp; /* upper side vnode */
|
||||
struct vnode *un_dvp; /* parent unionfs vnode */
|
||||
struct vnode *un_vnode; /* Back pointer */
|
||||
LIST_HEAD(, unionfs_node_status) un_unshead; /* unionfs status head */
|
||||
char *un_path; /* path */
|
||||
int un_flag; /* unionfs node flag */
|
||||
};
|
||||
|
||||
#define UNVP_WANT 0x01
|
||||
#define UNVP_LOCKED 0x02
|
||||
/* unionfs node flags */
|
||||
#define UNIONFS_CACHED 0x01 /* is cached */
|
||||
#define UNIONFS_OPENEXTL 0x02 /* openextattr (lower) */
|
||||
#define UNIONFS_OPENEXTU 0x04 /* openextattr (upper) */
|
||||
|
||||
extern int union_allocvp(struct vnode **, struct mount *,
|
||||
struct vnode *,
|
||||
struct vnode *,
|
||||
struct componentname *, struct vnode *,
|
||||
struct vnode *, int);
|
||||
extern int union_freevp(struct vnode *);
|
||||
extern struct vnode *union_dircache_get(struct vnode *, struct thread *);
|
||||
extern void union_dircache_free(struct union_node *);
|
||||
extern int union_copyup(struct union_node *, int, struct ucred *,
|
||||
struct thread *);
|
||||
extern int union_dowhiteout(struct union_node *, struct ucred *,
|
||||
struct thread *);
|
||||
extern int union_mkshadow(struct union_mount *, struct vnode *,
|
||||
struct componentname *, struct vnode **);
|
||||
extern int union_mkwhiteout(struct union_mount *, struct vnode *,
|
||||
struct componentname *, char *);
|
||||
extern int union_cn_close(struct vnode *, int, struct ucred *,
|
||||
struct thread *);
|
||||
extern void union_removed_upper(struct union_node *un);
|
||||
extern struct vnode *union_lowervp(struct vnode *);
|
||||
extern void union_newsize(struct vnode *, off_t, off_t);
|
||||
#define MOUNTTOUNIONFSMOUNT(mp) ((struct unionfs_mount *)((mp)->mnt_data))
|
||||
#define VTOUNIONFS(vp) ((struct unionfs_node *)(vp)->v_data)
|
||||
#define UNIONFSTOV(xp) ((xp)->un_vnode)
|
||||
|
||||
extern int (*union_dircheckp)(struct thread *, struct vnode **,
|
||||
struct file *);
|
||||
int unionfs_init(struct vfsconf *vfsp);
|
||||
int unionfs_uninit(struct vfsconf *vfsp);
|
||||
int unionfs_nodeget(struct mount *mp, struct vnode *uppervp, struct vnode *lowervp, struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, struct thread *td);
|
||||
void unionfs_hashrem(struct vnode *vp, struct thread *td);
|
||||
void unionfs_get_node_status(struct unionfs_node *unp, struct thread *td, struct unionfs_node_status **unspp);
|
||||
void unionfs_tryrem_node_status(struct unionfs_node *unp, struct thread *td, struct unionfs_node_status *unsp);
|
||||
|
||||
#define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
|
||||
#define VTOUNION(vp) ((struct union_node *)(vp)->v_data)
|
||||
#define UNIONTOV(un) ((un)->un_vnode)
|
||||
#define LOWERVP(vp) (VTOUNION(vp)->un_lowervp)
|
||||
#define UPPERVP(vp) (VTOUNION(vp)->un_uppervp)
|
||||
#define OTHERVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : LOWERVP(vp))
|
||||
int unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td);
|
||||
int unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred, struct thread *td);
|
||||
void unionfs_create_uppervattr_core(struct unionfs_mount *ump, struct vattr *lva, struct vattr *uva, struct thread *td);
|
||||
int unionfs_create_uppervattr(struct unionfs_mount *ump, struct vnode *lvp, struct vattr *uva, struct ucred *cred, struct thread *td);
|
||||
int unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *duvp, struct unionfs_node *unp, struct componentname *cnp, struct thread *td);
|
||||
int unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp, struct thread *td, char *path);
|
||||
int unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp, struct thread *td);
|
||||
int unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp, struct thread *td);
|
||||
int unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp, struct thread *td);
|
||||
|
||||
#define UDEBUG(x) if (uniondebug) printf x
|
||||
#define UDEBUG_ENABLED 1
|
||||
#ifdef DIAGNOSTIC
|
||||
struct vnode *unionfs_checklowervp(struct vnode *vp, char *fil, int lno);
|
||||
struct vnode *unionfs_checkuppervp(struct vnode *vp, char *fil, int lno);
|
||||
#define UNIONFSVPTOLOWERVP(vp) unionfs_checklowervp((vp), __FILE__, __LINE__)
|
||||
#define UNIONFSVPTOUPPERVP(vp) unionfs_checkuppervp((vp), __FILE__, __LINE__)
|
||||
#else
|
||||
#define UNIONFSVPTOLOWERVP(vp) (VTOUNIONFS(vp)->un_lowervp)
|
||||
#define UNIONFSVPTOUPPERVP(vp) (VTOUNIONFS(vp)->un_uppervp)
|
||||
#endif
|
||||
|
||||
extern struct vop_vector union_vnodeops;
|
||||
extern int uniondebug;
|
||||
extern struct vop_vector unionfs_vnodeops;
|
||||
|
||||
#endif /* _KERNEL */
|
||||
#ifdef MALLOC_DECLARE
|
||||
MALLOC_DECLARE(M_UNIONFSNODE);
|
||||
MALLOC_DECLARE(M_UNIONFSPATH);
|
||||
#endif
|
||||
|
||||
#ifdef UNIONFS_DEBUG
|
||||
#define UNIONFSDEBUG(format, args...) printf(format ,## args)
|
||||
#else
|
||||
#define UNIONFSDEBUG(format, args...)
|
||||
#endif /* UNIONFS_DEBUG */
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,8 @@
|
||||
/*-
|
||||
* Copyright (c) 1994, 1995 The Regents of the University of California.
|
||||
* Copyright (c) 1994, 1995 Jan-Simon Pendry.
|
||||
* Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
|
||||
* Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software donated to Berkeley by
|
||||
@ -34,437 +36,438 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Union Layer
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kdb.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/filedesc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <fs/unionfs/union.h>
|
||||
|
||||
static MALLOC_DEFINE(M_UNIONFSMNT, "union_mount", "UNION mount structure");
|
||||
static MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure");
|
||||
|
||||
extern vfs_init_t union_init;
|
||||
static vfs_root_t union_root;
|
||||
static vfs_mount_t union_mount;
|
||||
static vfs_statfs_t union_statfs;
|
||||
static vfs_unmount_t union_unmount;
|
||||
static vfs_fhtovp_t unionfs_fhtovp;
|
||||
static vfs_checkexp_t unionfs_checkexp;
|
||||
static vfs_mount_t unionfs_domount;
|
||||
static vfs_quotactl_t unionfs_quotactl;
|
||||
static vfs_root_t unionfs_root;
|
||||
static vfs_sync_t unionfs_sync;
|
||||
static vfs_statfs_t unionfs_statfs;
|
||||
static vfs_unmount_t unionfs_unmount;
|
||||
static vfs_vget_t unionfs_vget;
|
||||
static vfs_vptofh_t unionfs_vptofh;
|
||||
static vfs_extattrctl_t unionfs_extattrctl;
|
||||
|
||||
static struct vfsops unionfs_vfsops;
|
||||
|
||||
/*
|
||||
* Mount union filesystem.
|
||||
* Exchange from userland file mode to vmode.
|
||||
*/
|
||||
static u_short
|
||||
mode2vmode(mode_t mode)
|
||||
{
|
||||
u_short ret;
|
||||
|
||||
ret = 0;
|
||||
|
||||
/* other */
|
||||
if (mode & S_IXOTH)
|
||||
ret |= VEXEC >> 6;
|
||||
if (mode & S_IWOTH)
|
||||
ret |= VWRITE >> 6;
|
||||
if (mode & S_IROTH)
|
||||
ret |= VREAD >> 6;
|
||||
|
||||
/* group */
|
||||
if (mode & S_IXGRP)
|
||||
ret |= VEXEC >> 3;
|
||||
if (mode & S_IWGRP)
|
||||
ret |= VWRITE >> 3;
|
||||
if (mode & S_IRGRP)
|
||||
ret |= VREAD >> 3;
|
||||
|
||||
/* owner */
|
||||
if (mode & S_IXUSR)
|
||||
ret |= VEXEC;
|
||||
if (mode & S_IWUSR)
|
||||
ret |= VWRITE;
|
||||
if (mode & S_IRUSR)
|
||||
ret |= VREAD;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mount unionfs layer.
|
||||
*/
|
||||
static int
|
||||
union_mount(mp, td)
|
||||
struct mount *mp;
|
||||
struct thread *td;
|
||||
unionfs_domount(struct mount *mp, struct thread *td)
|
||||
{
|
||||
int error = 0;
|
||||
struct vfsoptlist *opts;
|
||||
struct vnode *lowerrootvp = NULLVP;
|
||||
struct vnode *upperrootvp = NULLVP;
|
||||
struct union_mount *um = 0;
|
||||
struct vattr va;
|
||||
char *cp = 0, *target;
|
||||
int op;
|
||||
int len;
|
||||
size_t size;
|
||||
int error;
|
||||
struct vnode *lowerrootvp;
|
||||
struct vnode *upperrootvp;
|
||||
struct unionfs_mount *ump;
|
||||
char *target;
|
||||
char *tmp;
|
||||
char *ep;
|
||||
int len;
|
||||
size_t done;
|
||||
int below;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
u_short udir;
|
||||
u_short ufile;
|
||||
unionfs_copymode copymode;
|
||||
struct componentname fakecn;
|
||||
struct nameidata nd, *ndp = &nd;
|
||||
struct nameidata nd, *ndp;
|
||||
struct vattr va;
|
||||
|
||||
UDEBUG(("union_mount(mp = %p)\n", (void *)mp));
|
||||
UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp);
|
||||
|
||||
opts = mp->mnt_optnew;
|
||||
/*
|
||||
* Disable clustered write, otherwise system becomes unstable.
|
||||
*/
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag |= MNT_NOCLUSTERW;
|
||||
MNT_IUNLOCK(mp);
|
||||
error = 0;
|
||||
below = 0;
|
||||
uid = 0;
|
||||
gid = 0;
|
||||
udir = 0;
|
||||
ufile = 0;
|
||||
copymode = UNIONFS_TRADITIONAL; /* default */
|
||||
ndp = &nd;
|
||||
|
||||
if (mp->mnt_flag & MNT_ROOTFS)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
/*
|
||||
* Update is a no-op
|
||||
* Update is a no operation.
|
||||
*/
|
||||
if (mp->mnt_flag & MNT_UPDATE)
|
||||
/*
|
||||
* Need to provide:
|
||||
* 1. a way to convert between rdonly and rdwr mounts.
|
||||
* 2. support for nfs exports.
|
||||
*/
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
/*
|
||||
* Get arguments.
|
||||
* Get argument
|
||||
*/
|
||||
error = vfs_getopt(opts, "target", (void **)&target, &len);
|
||||
if (error || target[len - 1] != '\0')
|
||||
return (EINVAL);
|
||||
|
||||
op = 0;
|
||||
if (vfs_getopt(opts, "below", NULL, NULL) == 0)
|
||||
op = UNMNT_BELOW;
|
||||
if (vfs_getopt(opts, "replace", NULL, NULL) == 0) {
|
||||
/* These options are mutually exclusive. */
|
||||
if (op)
|
||||
return (EINVAL);
|
||||
op = UNMNT_REPLACE;
|
||||
}
|
||||
/*
|
||||
* UNMNT_ABOVE is the default.
|
||||
*/
|
||||
if (op == 0)
|
||||
op = UNMNT_ABOVE;
|
||||
|
||||
/*
|
||||
* Obtain lower vnode. Vnode is stored in mp->mnt_vnodecovered.
|
||||
* We need to reference it but not lock it.
|
||||
*/
|
||||
lowerrootvp = mp->mnt_vnodecovered;
|
||||
VREF(lowerrootvp);
|
||||
/*
|
||||
* Obtain upper vnode by calling namei() on the path. The
|
||||
* upperrootvp will be turned referenced and locked.
|
||||
*/
|
||||
NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, td);
|
||||
error = namei(ndp);
|
||||
error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
|
||||
if (error)
|
||||
goto bad;
|
||||
error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target,
|
||||
&len);
|
||||
if (error || target[len - 1] != '\0') {
|
||||
vfs_mount_error(mp, "Invalid target");
|
||||
return (EINVAL);
|
||||
}
|
||||
if (vfs_getopt(mp->mnt_optnew, "below", NULL, NULL) == 0)
|
||||
below = 1;
|
||||
if (vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, NULL) == 0) {
|
||||
if (tmp != NULL)
|
||||
udir = (mode_t)strtol(tmp, &ep, 8);
|
||||
if (tmp == NULL || *ep) {
|
||||
vfs_mount_error(mp, "Invalid udir");
|
||||
return (EINVAL);
|
||||
}
|
||||
udir = mode2vmode(udir);
|
||||
}
|
||||
if (vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, NULL) == 0) {
|
||||
if (tmp != NULL)
|
||||
ufile = (mode_t)strtol(tmp, &ep, 8);
|
||||
if (tmp == NULL || *ep) {
|
||||
vfs_mount_error(mp, "Invalid ufile");
|
||||
return (EINVAL);
|
||||
}
|
||||
ufile = mode2vmode(ufile);
|
||||
}
|
||||
/* check umask, uid and gid */
|
||||
if (udir == 0 && ufile != 0)
|
||||
udir = ufile;
|
||||
if (ufile == 0 && udir != 0)
|
||||
ufile = udir;
|
||||
|
||||
vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY, td);
|
||||
error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred, td);
|
||||
if (!error) {
|
||||
if (udir == 0)
|
||||
udir = va.va_mode;
|
||||
if (ufile == 0)
|
||||
ufile = va.va_mode;
|
||||
uid = va.va_uid;
|
||||
gid = va.va_gid;
|
||||
}
|
||||
VOP_UNLOCK(mp->mnt_vnodecovered, 0, td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (mp->mnt_cred->cr_ruid == 0) { /* root only */
|
||||
if (vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp,
|
||||
NULL) == 0) {
|
||||
if (tmp != NULL)
|
||||
uid = (uid_t)strtol(tmp, &ep, 10);
|
||||
if (tmp == NULL || *ep) {
|
||||
vfs_mount_error(mp, "Invalid uid");
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
if (vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp,
|
||||
NULL) == 0) {
|
||||
if (tmp != NULL)
|
||||
gid = (gid_t)strtol(tmp, &ep, 10);
|
||||
if (tmp == NULL || *ep) {
|
||||
vfs_mount_error(mp, "Invalid gid");
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp,
|
||||
NULL) == 0) {
|
||||
if (tmp == NULL) {
|
||||
vfs_mount_error(mp, "Invalid copymode");
|
||||
return (EINVAL);
|
||||
} else if (strcasecmp(tmp, "traditional") == 0)
|
||||
copymode = UNIONFS_TRADITIONAL;
|
||||
else if (strcasecmp(tmp, "transparent") == 0)
|
||||
copymode = UNIONFS_TRANSPARENT;
|
||||
else if (strcasecmp(tmp, "masquerade") == 0)
|
||||
copymode = UNIONFS_MASQUERADE;
|
||||
else {
|
||||
vfs_mount_error(mp, "Invalid copymode");
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */
|
||||
if (copymode == UNIONFS_TRADITIONAL) {
|
||||
uid = mp->mnt_cred->cr_ruid;
|
||||
gid = mp->mnt_cred->cr_rgid;
|
||||
}
|
||||
|
||||
UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid);
|
||||
UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile);
|
||||
UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode);
|
||||
|
||||
/*
|
||||
* Find upper node
|
||||
*/
|
||||
NDINIT(ndp, LOOKUP, FOLLOW | WANTPARENT | LOCKLEAF, UIO_SYSSPACE, target, td);
|
||||
if ((error = namei(ndp)))
|
||||
return (error);
|
||||
|
||||
NDFREE(ndp, NDF_ONLY_PNBUF);
|
||||
|
||||
/* get root vnodes */
|
||||
lowerrootvp = mp->mnt_vnodecovered;
|
||||
upperrootvp = ndp->ni_vp;
|
||||
|
||||
UDEBUG(("mount_root UPPERVP %p locked = %d\n", upperrootvp,
|
||||
VOP_ISLOCKED(upperrootvp, NULL)));
|
||||
vrele(ndp->ni_dvp);
|
||||
ndp->ni_dvp = NULLVP;
|
||||
|
||||
/* create unionfs_mount */
|
||||
ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount),
|
||||
M_UNIONFSMNT, M_WAITOK | M_ZERO);
|
||||
|
||||
/*
|
||||
* Check multi union mount to avoid `lock myself again' panic.
|
||||
* Also require that it be a directory.
|
||||
* Save reference
|
||||
*/
|
||||
if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) {
|
||||
#ifdef DIAGNOSTIC
|
||||
printf("union_mount: multi union mount?\n");
|
||||
#endif
|
||||
error = EDEADLK;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (upperrootvp->v_type != VDIR) {
|
||||
error = EINVAL;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate our union_mount structure and populate the fields.
|
||||
* The vnode references are stored in the union_mount as held,
|
||||
* unlocked references. Depending on the _BELOW flag, the
|
||||
* filesystems are viewed in a different order. In effect this
|
||||
* is the same as providing a mount-under option to the mount
|
||||
* syscall.
|
||||
*/
|
||||
|
||||
um = (struct union_mount *) malloc(sizeof(struct union_mount),
|
||||
M_UNIONFSMNT, M_WAITOK | M_ZERO);
|
||||
|
||||
um->um_op = op;
|
||||
|
||||
error = VOP_GETATTR(upperrootvp, &va, td->td_ucred, td);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
um->um_upperdev = va.va_fsid;
|
||||
|
||||
switch (um->um_op) {
|
||||
case UNMNT_ABOVE:
|
||||
um->um_lowervp = lowerrootvp;
|
||||
um->um_uppervp = upperrootvp;
|
||||
upperrootvp = NULL;
|
||||
lowerrootvp = NULL;
|
||||
break;
|
||||
|
||||
case UNMNT_BELOW:
|
||||
if (below) {
|
||||
VOP_UNLOCK(upperrootvp, 0, td);
|
||||
vn_lock(lowerrootvp, LK_RETRY|LK_EXCLUSIVE, td);
|
||||
um->um_lowervp = upperrootvp;
|
||||
um->um_uppervp = lowerrootvp;
|
||||
upperrootvp = NULL;
|
||||
lowerrootvp = NULL;
|
||||
break;
|
||||
|
||||
case UNMNT_REPLACE:
|
||||
vrele(lowerrootvp);
|
||||
lowerrootvp = NULL;
|
||||
um->um_uppervp = upperrootvp;
|
||||
um->um_lowervp = lowerrootvp;
|
||||
upperrootvp = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = EINVAL;
|
||||
goto bad;
|
||||
vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td);
|
||||
ump->um_lowervp = upperrootvp;
|
||||
ump->um_uppervp = lowerrootvp;
|
||||
} else {
|
||||
ump->um_lowervp = lowerrootvp;
|
||||
ump->um_uppervp = upperrootvp;
|
||||
}
|
||||
ump->um_rootvp = NULLVP;
|
||||
ump->um_uid = uid;
|
||||
ump->um_gid = gid;
|
||||
ump->um_udir = udir;
|
||||
ump->um_ufile = ufile;
|
||||
ump->um_copymode = copymode;
|
||||
|
||||
mp->mnt_data = (qaddr_t)ump;
|
||||
|
||||
/*
|
||||
* Unless the mount is readonly, ensure that the top layer
|
||||
* supports whiteout operations.
|
||||
* Copy upper layer's RDONLY flag.
|
||||
*/
|
||||
mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY;
|
||||
|
||||
/*
|
||||
* Check whiteout
|
||||
*/
|
||||
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
|
||||
/*
|
||||
* XXX Fake up a struct componentname with only cn_nameiop
|
||||
* and cn_thread valid; union_whiteout() needs to use the
|
||||
* thread pointer to lock the vnode.
|
||||
*/
|
||||
bzero(&fakecn, sizeof(fakecn));
|
||||
memset(&fakecn, 0, sizeof(fakecn));
|
||||
fakecn.cn_nameiop = LOOKUP;
|
||||
fakecn.cn_thread = td;
|
||||
error = VOP_WHITEOUT(um->um_uppervp, &fakecn, LOOKUP);
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
VOP_UNLOCK(um->um_uppervp, 0, td);
|
||||
|
||||
um->um_cred = crhold(td->td_ucred);
|
||||
FILEDESC_LOCK_FAST(td->td_proc->p_fd);
|
||||
um->um_cmode = UN_DIRMODE &~ td->td_proc->p_fd->fd_cmask;
|
||||
FILEDESC_UNLOCK_FAST(td->td_proc->p_fd);
|
||||
|
||||
/*
|
||||
* Depending on what you think the MNT_LOCAL flag might mean,
|
||||
* you may want the && to be || on the conditional below.
|
||||
* At the moment it has been defined that the filesystem is
|
||||
* only local if it is all local, ie the MNT_LOCAL flag implies
|
||||
* that the entire namespace is local. If you think the MNT_LOCAL
|
||||
* flag implies that some of the files might be stored locally
|
||||
* then you will want to change the conditional.
|
||||
*/
|
||||
if (um->um_op == UNMNT_ABOVE) {
|
||||
if (((um->um_lowervp == NULLVP) ||
|
||||
(um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
|
||||
(um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) {
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag |= MNT_LOCAL;
|
||||
MNT_IUNLOCK(mp);
|
||||
error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP);
|
||||
if (error) {
|
||||
if (below) {
|
||||
VOP_UNLOCK(ump->um_uppervp, 0, td);
|
||||
vrele(upperrootvp);
|
||||
} else
|
||||
vput(ump->um_uppervp);
|
||||
free(ump, M_UNIONFSMNT);
|
||||
mp->mnt_data = NULL;
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy in the upper layer's RDONLY flag. This is for the benefit
|
||||
* of lookup() which explicitly checks the flag, rather than asking
|
||||
* the filesystem for its own opinion. This means, that an update
|
||||
* mount of the underlying filesystem to go from rdonly to rdwr
|
||||
* will leave the unioned view as read-only.
|
||||
* Unlock the node
|
||||
*/
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
|
||||
MNT_IUNLOCK(mp);
|
||||
VOP_UNLOCK(ump->um_uppervp, 0, td);
|
||||
|
||||
mp->mnt_data = (qaddr_t) um;
|
||||
/*
|
||||
* Get the unionfs root vnode.
|
||||
*/
|
||||
error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp,
|
||||
NULLVP, &(ump->um_rootvp), NULL, td);
|
||||
if (error) {
|
||||
vrele(upperrootvp);
|
||||
free(ump, M_UNIONFSMNT);
|
||||
mp->mnt_data = NULL;
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check mnt_flag
|
||||
*/
|
||||
if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) &&
|
||||
(ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
|
||||
mp->mnt_flag |= MNT_LOCAL;
|
||||
|
||||
/*
|
||||
* Get new fsid
|
||||
*/
|
||||
vfs_getnewfsid(mp);
|
||||
|
||||
switch (um->um_op) {
|
||||
case UNMNT_ABOVE:
|
||||
cp = "<above>:";
|
||||
break;
|
||||
case UNMNT_BELOW:
|
||||
cp = "<below>:";
|
||||
break;
|
||||
case UNMNT_REPLACE:
|
||||
cp = "";
|
||||
break;
|
||||
}
|
||||
len = strlen(cp);
|
||||
bcopy(cp, mp->mnt_stat.f_mntfromname, len);
|
||||
len = MNAMELEN - 1;
|
||||
tmp = mp->mnt_stat.f_mntfromname;
|
||||
copystr((below ? "<below>:" : "<above>:"), tmp, len, &done);
|
||||
len -= done - 1;
|
||||
tmp += done - 1;
|
||||
copystr(target, tmp, len, NULL);
|
||||
|
||||
cp = mp->mnt_stat.f_mntfromname + len;
|
||||
len = MNAMELEN - len;
|
||||
UNIONFSDEBUG("unionfs_mount: from %s, on %s\n",
|
||||
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
|
||||
|
||||
(void) copystr(target, cp, len - 1, &size);
|
||||
bzero(cp + size, len - size);
|
||||
|
||||
UDEBUG(("union_mount: from %s, on %s\n",
|
||||
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname));
|
||||
return (0);
|
||||
|
||||
bad:
|
||||
if (um) {
|
||||
if (um->um_uppervp)
|
||||
vput(um->um_uppervp);
|
||||
if (um->um_lowervp)
|
||||
vrele(um->um_lowervp);
|
||||
/* XXX other fields */
|
||||
free(um, M_UNIONFSMNT);
|
||||
}
|
||||
if (upperrootvp)
|
||||
vput(upperrootvp);
|
||||
if (lowerrootvp)
|
||||
vrele(lowerrootvp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free reference to union layer.
|
||||
* Free reference to unionfs layer
|
||||
*/
|
||||
static int
|
||||
union_unmount(mp, mntflags, td)
|
||||
struct mount *mp;
|
||||
int mntflags;
|
||||
struct thread *td;
|
||||
unionfs_unmount(struct mount *mp, int mntflags, struct thread *td)
|
||||
{
|
||||
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
|
||||
int error;
|
||||
int freeing;
|
||||
int flags = 0;
|
||||
struct unionfs_mount *ump;
|
||||
int error;
|
||||
int num;
|
||||
int freeing;
|
||||
int flags;
|
||||
|
||||
UDEBUG(("union_unmount(mp = %p)\n", (void *)mp));
|
||||
UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp);
|
||||
|
||||
ump = MOUNTTOUNIONFSMOUNT(mp);
|
||||
flags = 0;
|
||||
|
||||
if (mntflags & MNT_FORCE)
|
||||
flags |= FORCECLOSE;
|
||||
|
||||
/*
|
||||
* Keep flushing vnodes from the mount list.
|
||||
* This is needed because of the un_pvp held
|
||||
* reference to the parent vnode.
|
||||
* If more vnodes have been freed on a given pass,
|
||||
* the try again. The loop will iterate at most
|
||||
* (d) times, where (d) is the maximum tree depth
|
||||
* in the filesystem.
|
||||
*/
|
||||
for (freeing = 0; (error = vflush(mp, 0, flags, td)) != 0;) {
|
||||
int n;
|
||||
|
||||
/* count #vnodes held on mount list */
|
||||
n = mp->mnt_nvnodelistsize;
|
||||
|
||||
/* if this is unchanged then stop */
|
||||
if (n == freeing)
|
||||
/* vflush (no need to call vrele) */
|
||||
for (freeing = 0; (error = vflush(mp, 1, flags, td)) != 0;) {
|
||||
num = mp->mnt_nvnodelistsize;
|
||||
if (num == freeing)
|
||||
break;
|
||||
|
||||
/* otherwise try once more time */
|
||||
freeing = n;
|
||||
freeing = num;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the most recent vflush failed, the filesystem is still busy.
|
||||
*/
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Discard references to upper and lower target vnodes.
|
||||
*/
|
||||
if (um->um_lowervp)
|
||||
vrele(um->um_lowervp);
|
||||
vrele(um->um_uppervp);
|
||||
crfree(um->um_cred);
|
||||
/*
|
||||
* Finally, throw away the union_mount structure.
|
||||
*/
|
||||
free(mp->mnt_data, M_UNIONFSMNT); /* XXX */
|
||||
free(ump, M_UNIONFSMNT);
|
||||
mp->mnt_data = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
union_root(mp, flags, vpp, td)
|
||||
struct mount *mp;
|
||||
int flags;
|
||||
struct vnode **vpp;
|
||||
struct thread *td;
|
||||
unionfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
|
||||
{
|
||||
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
|
||||
int error;
|
||||
struct unionfs_mount *ump;
|
||||
struct unionfs_node *unp;
|
||||
struct vnode *vp;
|
||||
|
||||
/*
|
||||
* Supply an unlocked reference to um_uppervp and to um_lowervp. It
|
||||
* is possible for um_uppervp to be locked without the associated
|
||||
* root union_node being locked. We let union_allocvp() deal with
|
||||
* it.
|
||||
*/
|
||||
UDEBUG(("union_root UPPERVP %p locked = %d\n", um->um_uppervp,
|
||||
VOP_ISLOCKED(um->um_uppervp, NULL)));
|
||||
ump = MOUNTTOUNIONFSMOUNT(mp);
|
||||
vp = ump->um_rootvp;
|
||||
unp = VTOUNIONFS(vp);
|
||||
|
||||
VREF(um->um_uppervp);
|
||||
if (um->um_lowervp)
|
||||
VREF(um->um_lowervp);
|
||||
UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n",
|
||||
vp, VOP_ISLOCKED(vp, td));
|
||||
|
||||
error = union_allocvp(vpp, mp, NULLVP, NULLVP, NULL,
|
||||
um->um_uppervp, um->um_lowervp, 1);
|
||||
UDEBUG(("error %d\n", error));
|
||||
UDEBUG(("union_root2 UPPERVP %p locked = %d\n", um->um_uppervp,
|
||||
VOP_ISLOCKED(um->um_uppervp, NULL)));
|
||||
vref(vp);
|
||||
if (flags & LK_TYPE_MASK)
|
||||
vn_lock(vp, flags, td);
|
||||
|
||||
return (error);
|
||||
*vpp = vp;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
union_statfs(mp, sbp, td)
|
||||
struct mount *mp;
|
||||
struct statfs *sbp;
|
||||
struct thread *td;
|
||||
unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg,
|
||||
struct thread *td)
|
||||
{
|
||||
int error;
|
||||
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
|
||||
struct statfs mstat;
|
||||
int lbsize;
|
||||
struct unionfs_mount *ump;
|
||||
|
||||
UDEBUG(("union_statfs(mp = %p, lvp = %p, uvp = %p)\n",
|
||||
(void *)mp, (void *)um->um_lowervp, (void *)um->um_uppervp));
|
||||
ump = MOUNTTOUNIONFSMOUNT(mp);
|
||||
|
||||
/*
|
||||
* Writing is always performed to upper vnode.
|
||||
*/
|
||||
return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg, td));
|
||||
}
|
||||
|
||||
static int
|
||||
unionfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
|
||||
{
|
||||
struct unionfs_mount *ump;
|
||||
int error;
|
||||
struct statfs mstat;
|
||||
uint64_t lbsize;
|
||||
|
||||
ump = MOUNTTOUNIONFSMOUNT(mp);
|
||||
|
||||
UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n",
|
||||
(void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp);
|
||||
|
||||
bzero(&mstat, sizeof(mstat));
|
||||
|
||||
if (um->um_lowervp) {
|
||||
error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, td);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
error = VFS_STATFS(ump->um_lowervp->v_mount, &mstat, td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* now copy across the "interesting" information and fake the rest */
|
||||
sbp->f_blocks = mstat.f_blocks;
|
||||
sbp->f_files = mstat.f_files;
|
||||
|
||||
lbsize = mstat.f_bsize;
|
||||
|
||||
error = VFS_STATFS(ump->um_uppervp->v_mount, &mstat, td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Now copy across the "interesting" information and fake the rest.
|
||||
* The FS type etc is copy from upper vfs.
|
||||
* (write able vfs have priority)
|
||||
*/
|
||||
#if 0
|
||||
sbp->f_type = mstat.f_type;
|
||||
sbp->f_flags = mstat.f_flags;
|
||||
sbp->f_bsize = mstat.f_bsize;
|
||||
sbp->f_iosize = mstat.f_iosize;
|
||||
#endif
|
||||
lbsize = mstat.f_bsize;
|
||||
sbp->f_blocks = mstat.f_blocks;
|
||||
sbp->f_bfree = mstat.f_bfree;
|
||||
sbp->f_bavail = mstat.f_bavail;
|
||||
sbp->f_files = mstat.f_files;
|
||||
sbp->f_ffree = mstat.f_ffree;
|
||||
|
||||
error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, td);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
sbp->f_flags = mstat.f_flags;
|
||||
sbp->f_bsize = mstat.f_bsize;
|
||||
sbp->f_iosize = mstat.f_iosize;
|
||||
|
||||
/*
|
||||
* If the lower and upper blocksizes differ, then frig the
|
||||
* block counts so that the sizes reported by df make some
|
||||
* kind of sense. None of this makes sense though.
|
||||
*/
|
||||
|
||||
if (mstat.f_bsize != lbsize)
|
||||
sbp->f_blocks = ((off_t) sbp->f_blocks * lbsize) / mstat.f_bsize;
|
||||
sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / mstat.f_bsize;
|
||||
|
||||
/*
|
||||
* The "total" fields count total resources in all layers,
|
||||
* the "free" fields count only those resources which are
|
||||
* free in the upper layer (since only the upper layer
|
||||
* is writeable).
|
||||
*/
|
||||
sbp->f_blocks += mstat.f_blocks;
|
||||
sbp->f_bfree = mstat.f_bfree;
|
||||
sbp->f_bavail = mstat.f_bavail;
|
||||
@ -473,12 +476,71 @@ union_statfs(mp, sbp, td)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct vfsops union_vfsops = {
|
||||
.vfs_init = union_init,
|
||||
.vfs_mount = union_mount,
|
||||
.vfs_root = union_root,
|
||||
.vfs_statfs = union_statfs,
|
||||
.vfs_unmount = union_unmount,
|
||||
static int
|
||||
unionfs_sync(struct mount *mp, int waitfor, struct thread *td)
|
||||
{
|
||||
/* nothing to do */
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
unionfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
unionfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
|
||||
struct ucred **credanonp)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
unionfs_vptofh(struct vnode *vp, struct fid *fhp)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
|
||||
int namespace, const char *attrname, struct thread *td)
|
||||
{
|
||||
struct unionfs_mount *ump;
|
||||
struct unionfs_node *unp;
|
||||
|
||||
ump = MOUNTTOUNIONFSMOUNT(mp);
|
||||
unp = VTOUNIONFS(filename_vp);
|
||||
|
||||
if (unp->un_uppervp != NULLVP) {
|
||||
return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd,
|
||||
unp->un_uppervp, namespace, attrname, td));
|
||||
} else {
|
||||
return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd,
|
||||
unp->un_lowervp, namespace, attrname, td));
|
||||
}
|
||||
}
|
||||
|
||||
static struct vfsops unionfs_vfsops = {
|
||||
.vfs_checkexp = unionfs_checkexp,
|
||||
.vfs_extattrctl = unionfs_extattrctl,
|
||||
.vfs_fhtovp = unionfs_fhtovp,
|
||||
.vfs_init = unionfs_init,
|
||||
.vfs_mount = unionfs_domount,
|
||||
.vfs_quotactl = unionfs_quotactl,
|
||||
.vfs_root = unionfs_root,
|
||||
.vfs_statfs = unionfs_statfs,
|
||||
.vfs_sync = unionfs_sync,
|
||||
.vfs_uninit = unionfs_uninit,
|
||||
.vfs_unmount = unionfs_unmount,
|
||||
.vfs_vget = unionfs_vget,
|
||||
.vfs_vptofh = unionfs_vptofh,
|
||||
};
|
||||
|
||||
VFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK);
|
||||
VFS_SET(unionfs_vfsops, unionfs, VFCF_LOOPBACK);
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user