diff options
Diffstat (limited to 'libdiskfs/fsys-getroot.c')
-rw-r--r-- | libdiskfs/fsys-getroot.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/libdiskfs/fsys-getroot.c b/libdiskfs/fsys-getroot.c new file mode 100644 index 00000000..4e44395d --- /dev/null +++ b/libdiskfs/fsys-getroot.c @@ -0,0 +1,203 @@ +/* + Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998 Free Software Foundation + +This file is part of the GNU Hurd. + +The GNU Hurd is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +The GNU Hurd is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the GNU Hurd; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Michael I. Bushnell. */ + +#include "priv.h" +#include "fsys_S.h" +#include <hurd/fsys.h> +#include <fcntl.h> + +/* Implement fsys_getroot as described in <hurd/fsys.defs>. */ +kern_return_t +diskfs_S_fsys_getroot (fsys_t controlport, + mach_port_t reply, + mach_msg_type_name_t replytype, + mach_port_t dotdot, + uid_t *uids, + u_int nuids, + uid_t *gids, + u_int ngids, + int flags, + retry_type *retry, + char *retryname, + file_t *returned_port, + mach_msg_type_name_t *returned_port_poly) +{ + struct port_info *pt = ports_lookup_port (diskfs_port_bucket, controlport, + diskfs_control_class); + error_t error = 0; + mode_t type; + struct protid *newpi; + struct iouser user; + struct peropen peropen_context = + { + root_parent: dotdot, + shadow_root_parent: MACH_PORT_NULL, + shadow_root: _diskfs_chroot_directory ? diskfs_root_node : NULL /* XXX */ + }; + + if (!pt) + return EOPNOTSUPP; + + flags &= O_HURD; + + user.uids = make_idvec (); + user.gids = make_idvec (); + idvec_set_ids (user.uids, uids, nuids); + idvec_set_ids (user.gids, gids, ngids); +#define drop_idvec() idvec_free (user.gids); idvec_free (user.uids) + + rwlock_reader_lock (&diskfs_fsys_lock); + mutex_lock (&diskfs_root_node->lock); + + /* This code is similar (but not the same as) the code in + dir-pathtrans.c that does the same thing. Perhaps a way should + be found to share the logic. */ + + type = diskfs_root_node->dn_stat.st_mode & S_IFMT; + + if (((diskfs_root_node->dn_stat.st_mode & S_IPTRANS) + || fshelp_translated (&diskfs_root_node->transbox)) + && !(flags & O_NOTRANS)) + { + error = fshelp_fetch_root (&diskfs_root_node->transbox, + &peropen_context, dotdot, &user, flags, + _diskfs_translator_callback1, + _diskfs_translator_callback2, + retry, retryname, returned_port); + if (error != ENOENT) + { + mutex_unlock (&diskfs_root_node->lock); + rwlock_reader_unlock (&diskfs_fsys_lock); + drop_idvec (); + if (!error) + *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND; + return error; + } + + /* ENOENT means the translator was removed in the interim. */ + error = 0; + } + + if (type == S_IFLNK && !(flags & (O_NOLINK | O_NOTRANS))) + { + /* Handle symlink interpretation */ + char pathbuf[diskfs_root_node->dn_stat.st_size + 1]; + int amt; + + if (diskfs_read_symlink_hook) + error = (*diskfs_read_symlink_hook) (diskfs_root_node, pathbuf); + if (!diskfs_read_symlink_hook || error == EINVAL) + error = diskfs_node_rdwr (diskfs_root_node, pathbuf, 0, + diskfs_root_node->dn_stat.st_size, 0, + 0, &amt); + pathbuf[amt] = '\0'; + + mutex_unlock (&diskfs_root_node->lock); + rwlock_reader_unlock (&diskfs_fsys_lock); + if (error) + { + drop_idvec (); + return error; + } + + if (pathbuf[0] == '/') + { + *retry = FS_RETRY_MAGICAL; + *returned_port = MACH_PORT_NULL; + *returned_port_poly = MACH_MSG_TYPE_COPY_SEND; + strcpy (retryname, pathbuf); + mach_port_deallocate (mach_task_self (), dotdot); + drop_idvec (); + return 0; + } + else + { + *retry = FS_RETRY_REAUTH; + *returned_port = dotdot; + *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND; + strcpy (retryname, pathbuf); + drop_idvec (); + return 0; + } + } + + if ((type == S_IFSOCK || type == S_IFBLK + || type == S_IFCHR || type == S_IFIFO) + && (flags & (O_READ|O_WRITE|O_EXEC))) + error = EOPNOTSUPP; + + if (!error && (flags & O_READ)) + error = fshelp_access (&diskfs_root_node->dn_stat, S_IREAD, &user); + + if (!error && (flags & O_EXEC)) + error = fshelp_access (&diskfs_root_node->dn_stat, S_IEXEC, &user); + + if (!error && (flags & (O_WRITE))) + { + if (type == S_IFDIR) + error = EISDIR; + else if (diskfs_check_readonly ()) + error = EROFS; + else + error = fshelp_access (&diskfs_root_node->dn_stat, + S_IWRITE, &user); + } + + if (error) + { + mutex_unlock (&diskfs_root_node->lock); + rwlock_reader_unlock (&diskfs_fsys_lock); + drop_idvec (); + return error; + } + + if ((flags & O_NOATIME) + && (fshelp_isowner (&diskfs_root_node->dn_stat, &user) + == EPERM)) + flags &= ~O_NOATIME; + + flags &= ~OPENONLY_STATE_MODES; + + error = + diskfs_create_protid (diskfs_make_peropen (diskfs_root_node, flags, + &peropen_context), + &user, &newpi); + + mach_port_deallocate (mach_task_self (), dotdot); + + if (! error) + { + *retry = FS_RETRY_NORMAL; + *retryname = '\0'; + *returned_port = ports_get_right (newpi); + *returned_port_poly = MACH_MSG_TYPE_MAKE_SEND; + ports_port_deref (newpi); + } + + mutex_unlock (&diskfs_root_node->lock); + rwlock_reader_unlock (&diskfs_fsys_lock); + + ports_port_deref (pt); + + drop_idvec (); + + return error; +} |