aboutsummaryrefslogtreecommitdiff
path: root/libdiskfs/dir-lookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdiskfs/dir-lookup.c')
-rw-r--r--libdiskfs/dir-lookup.c246
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;
}