diff options
Diffstat (limited to 'libdiskfs/dir-lookup.c')
-rw-r--r-- | libdiskfs/dir-lookup.c | 246 |
1 files changed, 162 insertions, 84 deletions
diff --git a/libdiskfs/dir-lookup.c b/libdiskfs/dir-lookup.c index c8138254..7e092908 100644 --- a/libdiskfs/dir-lookup.c +++ b/libdiskfs/dir-lookup.c @@ -1,5 +1,6 @@ /* libdiskfs implementation of fs.defs:dir_lookup - Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + 2002, 2008 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,16 +16,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "priv.h" -#include "fs_S.h" +#include <stdio.h> #include <fcntl.h> #include <string.h> +#include <sys/file.h> #include <hurd/fsys.h> #include <hurd/paths.h> -/* XXX - Temporary hack; this belongs in a header file, probably types.h. */ -#define major(x) ((int)(((unsigned) (x) >> 8) & 0xff)) -#define minor(x) ((int)((x) & 0xff)) +#include "priv.h" +#include "fs_S.h" /* Implement dir_lookup as described in <hurd/fs.defs>. */ kern_return_t @@ -51,15 +51,16 @@ diskfs_S_dir_lookup (struct protid *dircred, int newnode = 0; struct dirstat *ds = 0; int mustbedir = 0; - int amt; + size_t amt; int type; - struct protid *newpi; + struct protid *newpi = 0; + struct peropen *newpo = 0; if (!dircred) return EOPNOTSUPP; flags &= O_HURD; - + create = (flags & O_CREAT); excl = (flags & O_EXCL); @@ -73,8 +74,6 @@ diskfs_S_dir_lookup (struct protid *dircred, if (path[0] == '\0') { - mustbedir = 1; - /* Set things up in the state expected by the code from gotit: on. */ dnp = 0; np = dircred->po->np; @@ -84,6 +83,7 @@ diskfs_S_dir_lookup (struct protid *dircred, } dnp = dircred->po->np; + mutex_lock (&dnp->lock); np = 0; @@ -92,7 +92,7 @@ diskfs_S_dir_lookup (struct protid *dircred, do { assert (!lastcomp); - + /* Find the name of the next pathname component */ nextname = index (path, '/'); @@ -114,14 +114,14 @@ diskfs_S_dir_lookup (struct protid *dircred, } else lastcomp = 1; - + np = 0; /* diskfs_lookup the next pathname component */ if (lastcomp && create) { - assert (!ds); - ds = alloca (diskfs_dirstat_size); + if (!ds) + ds = alloca (diskfs_dirstat_size); error = diskfs_lookup (dnp, path, CREATE, &np, ds, dircred); } else @@ -133,10 +133,37 @@ diskfs_S_dir_lookup (struct protid *dircred, /* If we get an error we're done */ if (error == EAGAIN) { - if (dircred->po->dotdotport != MACH_PORT_NULL) + if (dnp == dircred->po->shadow_root) + /* We're at the root of a shadow tree. */ + { + if (dircred->po->shadow_root_parent == MACH_PORT_NULL) + { + /* This is a shadow root with no parent, meaning + we should treat it as a virtual root disconnected + from its real .. directory. */ + error = 0; + np = dnp; + diskfs_nref (np); + } + else + { + /* Punt the client up to the shadow root parent. */ + *retry = FS_RETRY_REAUTH; + *returned_port = dircred->po->shadow_root_parent; + *returned_port_poly = MACH_MSG_TYPE_COPY_SEND; + if (! lastcomp) + strcpy (retryname, nextname); + error = 0; + goto out; + } + } + else if (dircred->po->root_parent != MACH_PORT_NULL) + /* We're at a real translator root; even if DIRCRED->po has a + shadow root, we can get here if its in a directory that was + renamed out from under it... */ { *retry = FS_RETRY_REAUTH; - *returned_port = dircred->po->dotdotport; + *returned_port = dircred->po->root_parent; *returned_port_poly = MACH_MSG_TYPE_COPY_SEND; if (!lastcomp) strcpy (retryname, nextname); @@ -144,6 +171,7 @@ diskfs_S_dir_lookup (struct protid *dircred, goto out; } else + /* We're at a REAL root, as in there's no way up from here. */ { error = 0; np = dnp; @@ -156,7 +184,7 @@ diskfs_S_dir_lookup (struct protid *dircred, { if (error == ENOENT) { - mode &= ~(S_IFMT | S_ISPARE | S_ISVTX); + mode &= ~(S_IFMT | S_ISPARE | S_ISVTX | S_ITRANS); mode |= S_IFREG; error = diskfs_create_node (dnp, path, mode, &np, dircred, ds); if (diskfs_synchronous) @@ -169,26 +197,27 @@ diskfs_S_dir_lookup (struct protid *dircred, else diskfs_drop_dirstat (dnp, ds); } - + if (error) goto out; /* If this is translated, start the translator (if necessary) and return. */ if ((((flags & O_NOTRANS) == 0) || !lastcomp) - && (np->istranslated + && ((np->dn_stat.st_mode & S_IPTRANS) || S_ISFIFO (np->dn_stat.st_mode) || S_ISCHR (np->dn_stat.st_mode) || S_ISBLK (np->dn_stat.st_mode) || fshelp_translated (&np->transbox))) { mach_port_t dirport; - + struct iouser *user; + /* A callback function for short-circuited translators. Symlink & ifsock are handled elsewhere. */ error_t short_circuited_callback1 (void *cookie1, void *cookie2, uid_t *uid, gid_t *gid, - char **argz, int *argz_len) + char **argz, size_t *argz_len) { struct node *node = cookie1; @@ -221,25 +250,33 @@ diskfs_S_dir_lookup (struct protid *dircred, /* Create an unauthenticated port for DNP, and then unlock it. */ - error = - diskfs_create_protid (diskfs_make_peropen (dnp, 0, - dircred->po->dotdotport), - 0, 0, 0, 0, &newpi); + error = iohelp_create_empty_iouser (&user); + if (! error) + { + error = diskfs_make_peropen (dnp, 0, dircred->po, &newpo); + if (! error) + { + error = diskfs_create_protid (newpo, user, &newpi); + if (! error) + newpo = 0; + } + + iohelp_free_iouser (user); + } + if (error) goto out; - dirport = ports_get_right (newpi); - mach_port_insert_right (mach_task_self (), dirport, dirport, - MACH_MSG_TYPE_MAKE_SEND); + dirport = ports_get_send_right (newpi); ports_port_deref (newpi); + newpi = 0; if (np != dnp) mutex_unlock (&dnp->lock); - error = fshelp_fetch_root (&np->transbox, &dircred->po->dotdotport, - dirport, dircred->uids, dircred->nuids, - dircred->gids, dircred->ngids, + error = fshelp_fetch_root (&np->transbox, dircred->po, + dirport, dircred->user, lastcomp ? flags : 0, - (np->istranslated + ((np->dn_stat.st_mode & S_IPTRANS) ? _diskfs_translator_callback1 : short_circuited_callback1), _diskfs_translator_callback2, @@ -256,8 +293,9 @@ diskfs_S_dir_lookup (struct protid *dircred, *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND; if (!lastcomp && !error) { - strcat (retryname, "/"); - strcat (retryname, nextname); + char *end = strchr (retryname, '\0'); + *end++ = '/'; + strcpy (end, nextname); } return error; } @@ -278,9 +316,11 @@ diskfs_S_dir_lookup (struct protid *dircred, } } } - + if (S_ISLNK (np->dn_stat.st_mode) - && !(lastcomp && (flags & (O_NOLINK|O_NOTRANS)))) + && (!lastcomp + || mustbedir /* "foo/" must see that foo points to a dir */ + || !(flags & (O_NOLINK|O_NOTRANS)))) { /* Handle symlink interpretation */ @@ -289,51 +329,70 @@ diskfs_S_dir_lookup (struct protid *dircred, error = ELOOP; goto out; } - + nextnamelen = nextname ? strlen (nextname) + 1 : 0; - newnamelen = nextnamelen + np->dn_stat.st_size + 1; + newnamelen = nextnamelen + np->dn_stat.st_size + 1 + 1; if (pathbuflen < newnamelen) { pathbuf = alloca (newnamelen); pathbuflen = newnamelen; } - + if (diskfs_read_symlink_hook) error = (*diskfs_read_symlink_hook)(np, pathbuf); if (!diskfs_read_symlink_hook || error == EINVAL) - error = diskfs_node_rdwr (np, pathbuf, - 0, np->dn_stat.st_size, 0, - dircred, &amt); - if (error) - goto out; - - if (nextname) { - pathbuf[np->dn_stat.st_size] = '/'; - bcopy (nextname, pathbuf + np->dn_stat.st_size + 1, - nextnamelen - 1); + error = diskfs_node_rdwr (np, pathbuf, + 0, np->dn_stat.st_size, 0, + dircred, &amt); + if (!error) + assert (amt == np->dn_stat.st_size); } - pathbuf[nextnamelen + np->dn_stat.st_size] = '\0'; + if (error) + goto out; - if (pathbuf[0] == '/') + if (np->dn_stat.st_size == 0) /* symlink to "" */ + path = nextname; + else { - /* Punt to the caller. */ - *retry = FS_RETRY_MAGICAL; - *returned_port = MACH_PORT_NULL; - strcpy (retryname, pathbuf); - goto out; + if (nextname) + { + pathbuf[np->dn_stat.st_size] = '/'; + memcpy (pathbuf + np->dn_stat.st_size + 1, + nextname, nextnamelen - 1); + } + pathbuf[nextnamelen + np->dn_stat.st_size] = '\0'; + + if (pathbuf[0] == '/') + { + /* Punt to the caller. */ + *retry = FS_RETRY_MAGICAL; + *returned_port = MACH_PORT_NULL; + memcpy (retryname, pathbuf, + nextnamelen + np->dn_stat.st_size + 1); + if (mustbedir) + { + retryname[nextnamelen + np->dn_stat.st_size] = '/'; + retryname[nextnamelen + np->dn_stat.st_size + 1] = '\0'; + } + goto out; + } + + path = pathbuf; } - - path = pathbuf; + if (lastcomp) - { - lastcomp = 0; - /* Symlinks to nonexistent files aren't allowed to cause - creation, so clear the flag here. */ - create = 0; - } + lastcomp = 0; + diskfs_nput (np); np = 0; + + if (path == 0) /* symlink to "" was the last component */ + { + np = dnp; + dnp = 0; + break; + } } else { @@ -352,7 +411,7 @@ diskfs_S_dir_lookup (struct protid *dircred, dnp = 0; } } while (path && *path); - + /* At this point, np is the node to return. If newnode is set, then we just created this node. */ @@ -364,20 +423,21 @@ diskfs_S_dir_lookup (struct protid *dircred, error = ENOTDIR; goto out; } - + if (!newnode) /* Check permissions on existing nodes, but not new ones. */ { - if ((type == S_IFSOCK || type == S_IFBLK || type == S_IFLNK - || type == S_IFCHR || type == S_IFIFO) - && (flags & (O_READ|O_WRITE|O_EXEC))) - error = EOPNOTSUPP; + if (((type == S_IFSOCK || type == S_IFBLK || type == S_IFCHR || + type == S_IFIFO) + && (flags & (O_READ|O_WRITE|O_EXEC))) + || (type == S_IFLNK && (flags & (O_WRITE|O_EXEC)))) + error = EACCES; if (!error && (flags & O_READ)) - error = diskfs_access (np, S_IREAD, dircred); + error = fshelp_access (&np->dn_stat, S_IREAD, dircred->user); if (!error && (flags & O_EXEC)) - error = diskfs_access (np, S_IEXEC, dircred); + error = fshelp_access (&np->dn_stat, S_IEXEC, dircred->user); if (!error && (flags & O_WRITE)) { @@ -386,29 +446,41 @@ diskfs_S_dir_lookup (struct protid *dircred, else if (diskfs_check_readonly ()) error = EROFS; else - error = diskfs_access (np, S_IWRITE, dircred); + error = fshelp_access (&np->dn_stat, S_IWRITE, dircred->user); } if (error) goto out; } - - if ((flags & O_NOATIME) && (diskfs_isowner (np, dircred) == EPERM)) + + if ((flags & O_NOATIME) + && (fshelp_isowner (&np->dn_stat, dircred->user) == EPERM)) flags &= ~O_NOATIME; - flags &= ~OPENONLY_STATE_MODES; - - error = diskfs_create_protid (diskfs_make_peropen (np, flags, - dircred->po->dotdotport), - dircred->uids, dircred->nuids, - dircred->gids, dircred->ngids, - &newpi); + error = diskfs_make_peropen (np, (flags &~OPENONLY_STATE_MODES), + dircred->po, &newpo); + + if (! error) + error = diskfs_create_protid (newpo, dircred->user, &newpi); + + if (! error) + { + newpo = 0; + if (flags & O_EXLOCK) + error = fshelp_acquire_lock (&np->userlock, &newpi->po->lock_status, + &np->lock, LOCK_EX); + else if (flags & O_SHLOCK) + error = fshelp_acquire_lock (&np->userlock, &newpi->po->lock_status, + &np->lock, LOCK_SH); + } + if (! error) { *returned_port = ports_get_right (newpi); ports_port_deref (newpi); + newpi = 0; } - + out: if (np) { @@ -419,5 +491,11 @@ diskfs_S_dir_lookup (struct protid *dircred, } if (dnp) diskfs_nput (dnp); + + if (newpi) + ports_port_deref (newpi); + if (newpo) + diskfs_release_peropen (newpo); + return error; } |