diff options
-rw-r--r-- | trans/fakeroot.c | 354 |
1 files changed, 230 insertions, 124 deletions
diff --git a/trans/fakeroot.c b/trans/fakeroot.c index d958e58f..967fb523 100644 --- a/trans/fakeroot.c +++ b/trans/fakeroot.c @@ -36,6 +36,8 @@ char *netfs_server_name = "fakeroot"; char *netfs_server_version = HURD_VERSION; int netfs_maxsymlinks = 16; /* arbitrary */ +static auth_t fakeroot_auth_port; + struct netnode { void **idport_locp; /* easy removal pointer in idport ihash */ @@ -56,8 +58,10 @@ struct mutex idport_ihash_lock = MUTEX_INITIALIZER; struct ihash idport_ihash; +/* Make a new virtual node. Always consumes the ports. */ static error_t -new_node (file_t file, mach_port_t idport, int openmodes, struct node **np) +new_node (file_t file, mach_port_t idport, int locked, int openmodes, + struct node **np) { error_t err; struct netnode *nn = calloc (1, sizeof *nn); @@ -66,6 +70,8 @@ new_node (file_t file, mach_port_t idport, int openmodes, struct node **np) mach_port_deallocate (mach_task_self (), file); if (idport != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), idport); + if (locked) + mutex_unlock (&idport_ihash_lock); return ENOMEM; } nn->file = file; @@ -76,6 +82,7 @@ new_node (file_t file, mach_port_t idport, int openmodes, struct node **np) { int fileno; mach_port_t fsidport; + assert (!locked); err = io_identity (file, &nn->idport, &fsidport, &fileno); if (err) { @@ -86,11 +93,18 @@ new_node (file_t file, mach_port_t idport, int openmodes, struct node **np) } *np = netfs_make_node (nn); if (*np == 0) - err = ENOMEM; + { + if (locked) + mutex_unlock (&idport_ihash_lock); + err = ENOMEM; + } else { - mutex_lock (&idport_ihash_lock); + if (!locked) + mutex_lock (&idport_ihash_lock); err = ihash_add (&idport_ihash, nn->idport, *np, &nn->idport_locp); + if (!err) + netfs_nref (*np); /* Return a reference to the caller. */ mutex_unlock (&idport_ihash_lock); } if (err) @@ -143,6 +157,214 @@ netfs_node_norefs (struct node *np) free (np); } +/* Given an existing node, make sure it has NEWMODES in its openmodes. + If not null, FILE is a port with those openmodes. */ +static error_t +check_openmodes (struct netnode *nn, int newmodes, file_t file) +{ + error_t err = 0; + + if (newmodes &~ nn->openmodes) + { + /* The user wants openmodes we haven't tried before. */ + + if (file != MACH_PORT_NULL && (nn->openmodes & ~newmodes)) + { + /* Intersecting sets. + We need yet another new peropen on this node. */ + mach_port_deallocate (mach_task_self (), file); + file = MACH_PORT_NULL; + } + if (file == MACH_PORT_NULL); + { + enum retry_type bad_retry; + char bad_retryname[1024]; /* XXX */ + if (file != MACH_PORT_NULL) + err = dir_lookup (nn->file, "", nn->openmodes | newmodes, 0, + &bad_retry, bad_retryname, &file); + if (!err && (bad_retry != FS_RETRY_NORMAL + || bad_retryname[0] != '\0')) + { + mach_port_deallocate (mach_task_self (), file); + err = EGRATUITOUS; + } + } + if (! err) + { + /* The new port has more openmodes than + the old one. We can just use it now. */ + mach_port_deallocate (mach_task_self (), nn->file); + nn->file = file; + nn->openmodes = newmodes; + } + } + else if (file != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), file); + + return err; +} + +/* This is called by netfs_S_fsys_getroot. */ +error_t +netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + return check_openmodes (np->nn, flags & (O_RDWR|O_EXEC), MACH_PORT_NULL); +} + +error_t +netfs_S_dir_lookup (struct protid *diruser, + char *filename, + int flags, + mode_t mode, + retry_type *do_retry, + char *retry_name, + mach_port_t *retry_port, + mach_msg_type_name_t *retry_port_type) +{ + struct node *dnp, *np; + error_t err; + struct protid *newpi; + struct iouser *user; + mach_port_t file; + mach_port_t idport, fsidport; + int fileno; + + if (!diruser) + return EOPNOTSUPP; + + dnp = diruser->po->np; + err = dir_lookup (dnp->nn->file, filename, + O_NOLINK | (flags + & (O_RDWR|O_EXEC|O_CREAT|O_EXCL|O_NONBLOCK)), + mode, do_retry, retry_name, &file); + if (err) + return err; + + switch (*do_retry) + { + case FS_RETRY_REAUTH: + { + mach_port_t ref = mach_reply_port (); + err = io_reauthenticate (file, ref, MACH_MSG_TYPE_MAKE_SEND); + if (! err) + { + mach_port_deallocate (mach_task_self (), file); + err = auth_user_authenticate (fakeroot_auth_port, ref, + MACH_MSG_TYPE_MAKE_SEND, + retry_port); + } + mach_port_destroy (mach_task_self (), ref); + if (err) + return err; + } + *do_retry = FS_RETRY_NORMAL; + /*FALLTHROUGH*/ + + case FS_RETRY_NORMAL: + case FS_RETRY_MAGICAL: + default: + if (file == MACH_PORT_NULL) + { + *retry_port = MACH_PORT_NULL; + *retry_port_type = MACH_MSG_TYPE_COPY_SEND; + return 0; + } + break; + } + + /* We have a new port to an underlying node. + Find or make our corresponding virtual node. */ + + np = 0; + err = io_identity (file, &idport, &fsidport, &fileno); + if (err) + mach_port_deallocate (mach_task_self (), file); + else + { + mach_port_deallocate (mach_task_self (), fsidport); + if (fsidport == netfs_fsys_identity) + { + /* Talking to ourselves! We just looked up one of our + own nodes. Find the node and return it. */ + struct protid *cred + = ports_lookup_port (netfs_port_bucket, file, + netfs_protid_class); + mach_port_deallocate (mach_task_self (), idport); + mach_port_deallocate (mach_task_self (), file); + if (cred == 0) + return EGRATUITOUS; + np = cred->po->np; + netfs_nref (np); + ports_port_deref (cred); + } + else + { + mutex_lock (&idport_ihash_lock); + np = ihash_find (&idport_ihash, idport); + if (np != 0) + { + /* We already know about this node. */ + mach_port_deallocate (mach_task_self (), idport); + mutex_lock (&np->lock); + err = check_openmodes (np->nn, (flags & (O_RDWR|O_EXEC)), file); + if (!err) + netfs_nref (np); + mutex_unlock (&np->lock); + mutex_unlock (&idport_ihash_lock); + } + else + err = new_node (file, idport, 1, flags, &np); + } + } + if (err) + return err; + + if (retry_name[0] == '\0' && *do_retry == FS_RETRY_NORMAL) + flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK); + else + flags = 0; + + err = iohelp_dup_iouser (&user, diruser->user); + if (!err) + { + newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), + user); + if (! newpi) + { + iohelp_free_iouser (user); + err = errno; + } + else + { + *retry_port = ports_get_right (newpi); + *retry_port_type = MACH_MSG_TYPE_MAKE_SEND; + ports_port_deref (newpi); + } + } + + netfs_nrele (np); + return err; +} + +/* These callbacks are used only by the standard netfs_S_dir_lookup, + which we do not use. But the shared library requires us to define them. */ +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **np) +{ + assert (! "should not be here"); + return EIEIO; +} + +error_t +netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **np) +{ + assert (! "should not be here"); + return EIEIO; +} + /* Make sure that NP->nn_stat is filled with the most current information. CRED identifies the user responsible for the operation. NP is locked. */ error_t @@ -321,104 +543,6 @@ netfs_attempt_syncfs (struct iouser *cred, int wait) return 0; } -/* Lookup NAME in DIR (which is locked) for USER; set *NP to the found name - upon return. If the name was not found, then return ENOENT. On any - error, clear *NP. (*NP, if found, should be locked and a reference to - it generated. This call should unlock DIR no matter what.) */ -error_t -netfs_attempt_lookup (struct iouser *user, struct node *dir, - char *name, struct node **np) -{ - error_t err; - int flags; - const file_t dirfile = dir->nn->file; - file_t file; - - /* We must unlock the directory before making RPCs to the underlying - filesystem in case they somehow wind up trying to refer back to one of - our nodes. The DIRFILE port will not change or die as long as DIR - lives, and our caller holds a reference keeping it alive. */ - mutex_unlock (&dir->lock); - - flags = O_RDWR|O_EXEC; - file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); - if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP - || errno == EROFS || errno == EISDIR)) - { - flags = O_RDWR; - file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); - } - if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP - || errno == EROFS || errno == EISDIR)) - { - flags = O_READ|O_EXEC; - file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); - } - if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP - || errno == EISDIR)) - { - flags = O_READ; - file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); - } - if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP - || errno == EISDIR)) - { - flags = 0; - file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); - } - *np = 0; - if (file == MACH_PORT_NULL) - err = errno; - else - { - mach_port_t idport, fsidport; - int fileno; - err = io_identity (file, &idport, &fsidport, &fileno); - if (err) - mach_port_deallocate (mach_task_self (), file); - else - { - mach_port_deallocate (mach_task_self (), fsidport); - if (fsidport == netfs_fsys_identity) - { - /* Talking to ourselves! We just looked up one of our own nodes. - Find the node and return it. */ - struct protid *cred = ports_lookup_port (netfs_port_bucket, file, - netfs_protid_class); - mach_port_deallocate (mach_task_self (), idport); - mach_port_deallocate (mach_task_self (), file); - if (cred == 0) - return EGRATUITOUS; - *np = cred->po->np; - netfs_nref (*np); - ports_port_deref (cred); - } - else - { - mutex_lock (&idport_ihash_lock); - *np = ihash_find (&idport_ihash, idport); - if (*np != 0) - { - /* We already know about this node. */ - mach_port_deallocate (mach_task_self (), idport); - mach_port_deallocate (mach_task_self (), file); - netfs_nref (*np); - mutex_unlock (&idport_ihash_lock); - } - else - { - mutex_unlock (&idport_ihash_lock); - err = new_node (file, idport, flags, np); - } - } - } - } - - if (*np) - mutex_lock (&(*np)->lock); - return err; -} - error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, char *name, mode_t mode) @@ -468,26 +592,13 @@ netfs_attempt_mkfile (struct iouser *user, struct node *dir, file_t newfile; error_t err = dir_mkfile (dir->nn->file, O_RDWR|O_EXEC, real_from_fake_mode (mode), &newfile); - if (err == 0) - err = new_node (newfile, MACH_PORT_NULL, O_RDWR|O_EXEC, np); mutex_unlock (&dir->lock); + if (err == 0) + err = new_node (newfile, MACH_PORT_NULL, 0, O_RDWR|O_EXEC, np); return err; } error_t -netfs_attempt_create_file (struct iouser *user, struct node *dir, - char *name, mode_t mode, struct node **np) -{ - file_t newfile = file_name_lookup_under (dir->nn->file, name, - O_CREAT|O_RDWR|O_EXEC, - real_from_fake_mode (mode)); - mutex_unlock (&dir->lock); - if (newfile == MACH_PORT_NULL) - return errno; - return new_node (newfile, MACH_PORT_NULL, O_RDWR|O_EXEC, np); -} - -error_t netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) { char transbuf[sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1]; @@ -512,13 +623,6 @@ netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) } error_t -netfs_check_open_permissions (struct iouser *user, struct node *np, - int flags, int newnode) -{ - return (flags & (O_READ|O_WRITE|O_EXEC) & ~np->nn->openmodes) ? EACCES : 0; -} - -error_t netfs_attempt_read (struct iouser *cred, struct node *np, off_t offset, size_t *len, void *data) { @@ -776,12 +880,14 @@ any user to open nodes regardless of permissions as is done for root." }; /* Parse our command line arguments (all none of them). */ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); + fakeroot_auth_port = getauth (); + task_get_bootstrap_port (mach_task_self (), &bootstrap); netfs_init (); /* Get our underlying node (we presume it's a directory) and use that to make the root node of the filesystem. */ - err = new_node (netfs_startup (bootstrap, O_READ), MACH_PORT_NULL, O_READ, + err = new_node (netfs_startup (bootstrap, O_READ), MACH_PORT_NULL, 0, O_READ, &netfs_root_node); if (err) error (5, err, "Cannot create root node"); |