From 459df2f3cf215a8712f55280e0617189c45ff518 Mon Sep 17 00:00:00 2001 From: Gianluca Guida Date: Tue, 24 May 2005 22:41:38 +0000 Subject: added file creation and unlinking --- ChangeLog | 16 +++ netfs.c | 230 ++++++++++++++++++++++++--------- node.c | 433 ++++++++++++++++++++++++++++++++++++-------------------------- node.h | 9 ++ options.c | 2 - 5 files changed, 447 insertions(+), 243 deletions(-) diff --git a/ChangeLog b/ChangeLog index e9ff7a9..ac17c7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2005-05-24 Gianluca Guida + + * netfs.c (netfs_attempt_unlink): Implemented. + (netfs_attempt_create_file_reduced): New function. + (netfs_S_dir_lookup): Initialized variable "error". + Added support for file creation. + (netfs_attempt_lookup_improved): Changed instruction flow, + return on error not continue on non-error. + Check user permission to open file before returning port. + * node.c (node_create, node_update, node_lookup_file, node_ulfs_free) + (node_ulfs_init, node_entries_get, node_create_root, node_init_root): + Changed instruction flow, return on error not continue on non-error. + (node_unlink_file): New function. + * node.h (node_unlink_file): New declaration. + (node_ulfs_iterate_reverse_unlocked): New macro. + 2005-01-31 Gianluca Guida * stow.c (stow_diradd): Handle --stow argument with missing / diff --git a/netfs.c b/netfs.c index 729cca4..2f80c13 100644 --- a/netfs.c +++ b/netfs.c @@ -1,5 +1,5 @@ /* Hurd unionfs - Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. Written by Moritz Schulte . This program is free software; you can redistribute it and/or @@ -234,7 +234,17 @@ error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name) { - return EOPNOTSUPP; + error_t err = 0; + + node_update (dir); + + err = fshelp_access (&dir->nn_stat, S_IWRITE, user); + if (err) + return err; + + err = node_unlink_file (dir, name); + + return err; } /* Attempt to rename the directory FROMDIR to TODIR. Note that neither @@ -298,6 +308,77 @@ netfs_attempt_create_file (struct iouser *user, struct node *dir, return EOPNOTSUPP; } +/* We use this local interface to attempt_create file since we are + using our own netfs_S_dir_lookup. */ +error_t +netfs_attempt_create_file_reduced (struct iouser *user, struct node *dir, + char *name, mode_t mode, int flags) +{ + mach_port_t p; + error_t err; + struct stat statbuf; + + node_update (dir); + + err = fshelp_access (&dir->nn_stat, S_IWRITE, user); + if (err) + goto exit; + + /* Special case for no UID processes (like login shell) */ + if ((!user->uids->ids) || (!user->uids->ids)) + { + err = EACCES; + goto exit; + } + + mutex_unlock (&dir->lock); + err = node_lookup_file (dir, name, flags | O_CREAT, + &p, &statbuf); + mutex_lock (&dir->lock); + + if (err) + goto exit; + + err = file_chmod (p, mode); + if (err) + { + port_dealloc (p); + node_unlink_file (dir, name); + goto exit; + } + + err = file_chown (p, user->uids->ids[0], user->gids->ids[0]); + if (err) + { + port_dealloc (p); + node_unlink_file (dir, name); + goto exit; + } + + err = io_stat (p, &statbuf); + + /* Check file permissions. */ + if (! err && (flags & O_READ)) + err = fshelp_access (&statbuf, S_IREAD, user); + if (! err && (flags & O_WRITE)) + err = fshelp_access (&statbuf, S_IWRITE, user); + if (! err && (flags & O_EXEC)) + err = fshelp_access (&statbuf, S_IEXEC, user); + + if (err) + { + port_dealloc (p); + node_unlink_file (dir, name); + goto exit; + } + + port_dealloc (p); + + exit: + mutex_unlock (&dir->lock); + return err; +} + /* Read the contents of locked node NP (a symlink), for USER, into BUF. */ error_t @@ -389,26 +470,35 @@ netfs_attempt_lookup_improved (struct iouser *user, struct node *dir, mutex_lock (&dir->nn->lnode->lock); err = fshelp_access (&dir->nn_stat, S_IEXEC, user); - if ((! err) && (! *name || ! strcmp (name, "."))) + if (err) + goto exit; + + + if (! *name || ! strcmp (name, ".")) { + /* The same node is wanted. */ *np = dir; netfs_nref (*np); + } - else if ((! err) && (! strcmp (name, ".."))) + else if (! strcmp (name, "..")) { + /* We have to get the according light node first. */ lnode_t *lnode = dir->nn->lnode; node_t *node; err = ncache_node_lookup (lnode->dir, &node); - if (! err) - { - *np = node; - } + if (err) + goto exit; + + *np = node; + } - else if (! err) + else { + lnode_t *dir_lnode = dir->nn->lnode; struct stat statbuf; lnode_t *lnode = NULL; @@ -421,13 +511,18 @@ netfs_attempt_lookup_improved (struct iouser *user, struct node *dir, /* We have to unlock this node while doing lookups. */ mutex_unlock (&dir_lnode->lock); mutex_unlock (&dir->lock); - if (! err) - err = node_lookup_file (dir, name, flags & ~O_NOLINK, - &p, &statbuf); + + err = node_lookup_file (dir, name, flags & ~(O_NOLINK|O_CREAT), + &p, &statbuf); + mutex_lock (&dir->lock); mutex_lock (&dir_lnode->lock); - if ((! err) && S_ISDIR (statbuf.st_mode)) + + if (err) + goto exit; + + if (S_ISDIR (statbuf.st_mode)) { node_t *node; @@ -441,54 +536,70 @@ netfs_attempt_lookup_improved (struct iouser *user, struct node *dir, if (err == ENOENT) { /* It does not exist, we have to create it. */ - err = lnode_create (name, &lnode); - if (! err) - lnode_install (dir_lnode, lnode); + if (err) + goto exit; + + lnode_install (dir_lnode, lnode); } + + /* Now we have a light node. */ + err = ncache_node_lookup (lnode, &node); + + /* This unlocks the node for us. */ + lnode_ref_remove (lnode); - if (! err) - /* Now we have a light node. */ - err = ncache_node_lookup (lnode, &node); - - if (lnode) - /* This unlocks the node for us. */ - lnode_ref_remove (lnode); - - if (! err) - { - /* Got the node. */ - *np = node; - } + if (err) + goto exit; + + /* Got the node. */ + *np = node; + } - else if (! err) + else { /* The found node is not a directory. */ + mach_port_t p_restricted; + if (! lastcomp) { /* We have not reached the last path component yet. */ port_dealloc (p); err = ENOTDIR; + goto exit; } - if (! err) - { - mach_port_t p_restricted; - /* A file node is successfully looked up. */ - err = io_restrict_auth (p, &p_restricted, - user->uids->ids, user->uids->num, - user->gids->ids, user->gids->num); + /* Check file permissions. */ + if (! err && (flags & O_READ)) + err = fshelp_access (&statbuf, S_IREAD, user); + if (! err && (flags & O_WRITE)) + err = fshelp_access (&statbuf, S_IWRITE, user); + if (! err && (flags & O_EXEC)) + err = fshelp_access (&statbuf, S_IEXEC, user); + + if (err) + { port_dealloc (p); - if (! err) - { - /* Successfully restricted. */ - *port = p_restricted; - *port_type = MACH_MSG_TYPE_MOVE_SEND; - } + goto exit; } + + + /* A file node is successfully looked up. */ + err = io_restrict_auth (p, &p_restricted, + user->uids->ids, user->uids->num, + user->gids->ids, user->gids->num); + port_dealloc (p); + + if (err) + goto exit; + + /* Successfully restricted. */ + *port = p_restricted; + *port_type = MACH_MSG_TYPE_MOVE_SEND; } } - + + exit: if (err) *np = NULL; @@ -531,7 +642,7 @@ netfs_S_dir_lookup (struct protid *diruser, int nsymlinks = 0; struct node *dnp, *np; char *nextname; - error_t error; + error_t error = 0; struct protid *newpi; struct iouser *user; @@ -633,34 +744,37 @@ netfs_S_dir_lookup (struct protid *diruser, else /* Attempt a lookup on the next pathname component. */ error = netfs_attempt_lookup_improved (diruser->user, dnp, - filename, &np, flags, lastcomp, + filename, &np, + flags, lastcomp, retry_port, retry_port_type); - + /* At this point, DNP is unlocked */ - + /* Implement O_EXCL flag here */ if (lastcomp && create && excl && !error && np) error = EEXIST; - + /* Create the new node if necessary */ if (lastcomp && create && error == ENOENT) { mode &= ~(S_IFMT | S_ISPARE | S_ISVTX); mode |= S_IFREG; mutex_lock (&dnp->lock); - /* FIXME, new interface needed! */ - error = netfs_attempt_create_file (diruser->user, dnp, - filename, mode, &np); - - /* If someone has already created the file (between our lookup - and this create) then we just got EEXIST. If we are - EXCL, that's fine; otherwise, we have to retry the lookup. */ - if (error == EEXIST && !excl) + + error = netfs_attempt_create_file_reduced (diruser->user, dnp, + filename, mode, flags); + + /* We retry lookup in two cases: + - we created the file and we have to get a valid port; + - someone has already created the file (between our lookup + and this create) then we just got EEXIST. If we are EXCL, + that's fine; otherwise, we have to retry the lookup. */ + if ((!error) || (error == EEXIST && !excl)) { mutex_lock (&dnp->lock); goto retry_lookup; } - + newnode = 1; } diff --git a/node.c b/node.c index 5ea698a..976d5d1 100644 --- a/node.c +++ b/node.c @@ -1,5 +1,5 @@ /* Hurd unionfs - Copyright (C) 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. Written by Moritz Schulte . This program is free software; you can redistribute it and/or @@ -52,33 +52,36 @@ node_create (lnode_t *lnode, node_t **node) debug_msg ("node_create for lnode: %s", lnode->name); if (! netnode_new) - err = ENOMEM; - else { - node_new = netfs_make_node (netnode_new); - if (! node_new) - { - err = ENOMEM; - free (netnode_new); - } - else - { - node_new->nn->ulfs = NULL; - err = node_ulfs_init (node_new); - if (err) - node_destroy (node_new); - else - { - lnode->node = node_new; - lnode_ref_add (lnode); - node_new->nn->lnode = lnode; - node_new->nn->flags = 0; - node_new->nn->ncache_next = NULL; - node_new->nn->ncache_prev = NULL; - *node = node_new; - } - } + err = ENOMEM; + return err; } + + node_new = netfs_make_node (netnode_new); + if (! node_new) + { + err = ENOMEM; + free (netnode_new); + return err; + } + + node_new->nn->ulfs = NULL; + + err = node_ulfs_init (node_new); + if (err) + { + node_destroy (node_new); + return err; + } + + lnode->node = node_new; + lnode_ref_add (lnode); + node_new->nn->lnode = lnode; + node_new->nn->flags = 0; + node_new->nn->ncache_next = NULL; + node_new->nn->ncache_prev = NULL; + *node = node_new; + return err; } @@ -105,58 +108,103 @@ node_update (node_t *node) error_t err = 0; char *path; + node_ulfs_t *root_ulfs; + struct stat stat; + file_t port; + int i = 0; + debug_msg ("node_update for lnode: %s", node->nn->lnode->name); - if (! node_is_root (node)) + if (node_is_root (node)) + return err; + + mutex_lock (&netfs_root_node->lock); + + err = lnode_path_construct (node->nn->lnode, &path); + if (err) + { + mutex_unlock (&netfs_root_node->lock); + return err; + } + + root_ulfs = netfs_root_node->nn->ulfs; + + node_ulfs_iterate_unlocked (node) { - mutex_lock (&netfs_root_node->lock); - err = lnode_path_construct (node->nn->lnode, &path); - if (! err) + + if (node_ulfs->flags & FLAG_NODE_ULFS_FIXED) { - node_ulfs_t *root_ulfs; - struct stat stat; - file_t port; - int i = 0; - - root_ulfs = netfs_root_node->nn->ulfs; - node_ulfs_iterate_unlocked (node) - { - if (! (node_ulfs->flags & FLAG_NODE_ULFS_FIXED)) - { - /* We really have to update the port. */ - if (port_valid (node_ulfs->port)) - port_dealloc (node_ulfs->port); - err = file_lookup ((root_ulfs + i)->port, path, - O_READ | O_NOTRANS, O_NOTRANS, - 0, &port, &stat); - if (! err) - { - if (stat.st_ino == underlying_node_stat.st_ino - && stat.st_fsid == underlying_node_stat.st_fsid) - /* It's OUR root node. */ - err = ELOOP; - else - { - port_dealloc (port); - err = file_lookup ((root_ulfs + i)->port, path, - O_READ, 0, 0, &port, &stat); - } - } - - if (err) - { - port = MACH_PORT_NULL; - err = 0; - } - node_ulfs->port = port; - } - i++; - } - free (path); - node->nn->flags |= FLAG_NODE_ULFS_UPTODATE; + i++; + continue; } - mutex_unlock (&netfs_root_node->lock); + + /* We really have to update the port. */ + if (port_valid (node_ulfs->port)) + port_dealloc (node_ulfs->port); + + err = file_lookup ((root_ulfs + i)->port, path, + O_READ | O_NOTRANS, O_NOTRANS, + 0, &port, &stat); + + if (err) + { + node_ulfs->port = MACH_PORT_NULL; + err = 0; + i++; + continue; + } + + if (stat.st_ino == underlying_node_stat.st_ino + && stat.st_fsid == underlying_node_stat.st_fsid) + /* It's OUR root node. */ + err = ELOOP; + else + { + port_dealloc (port); + err = file_lookup ((root_ulfs + i)->port, path, + O_READ, 0, 0, &port, &stat); + } + + if (err) + { + port = MACH_PORT_NULL; + err = 0; + } + node_ulfs->port = port; + + i++; + } + + free (path); + node->nn->flags |= FLAG_NODE_ULFS_UPTODATE; + + mutex_unlock (&netfs_root_node->lock); + + return err; +} + +/* Remove all files named NAME beneath DIR on the underlying filesystems + with FLAGS as openflags. */ +error_t +node_unlink_file (node_t *dir, char *name) +{ + error_t err = 0; + + /* Using reverse iteration still have issues. Infact, we could be + deleting a file in some underlying filesystem, and keeping those + after the first occurring error. + FIXME: Check BEFORE starting deletion. */ + + node_ulfs_iterate_reverse_unlocked (dir) + { + + if (!port_valid (node_ulfs->port)) + continue; + + err = dir_unlink (node_ulfs->port, name); + } + err = 0; return err; } @@ -173,31 +221,38 @@ node_lookup_file (node_t *dir, char *name, int flags, node_ulfs_iterate_unlocked (dir) { - if (err == ENOENT && port_valid (node_ulfs->port)) + + if (err != ENOENT) + break; + + if (!port_valid (node_ulfs->port)) + continue; + + err = file_lookup (node_ulfs->port, name, + flags | O_NOTRANS, O_NOTRANS, + 0, &p, &stat); + if (err) + continue; + + if (stat.st_ino == underlying_node_stat.st_ino + && stat.st_fsid == underlying_node_stat.st_fsid) + /* It's OUR root node. */ + err = ELOOP; + else + /* stat.st_mode & S_ITRANS */ { + port_dealloc (p); err = file_lookup (node_ulfs->port, name, - flags | O_NOTRANS, O_NOTRANS, - 0, &p, &stat); - if (! err) - { - if (stat.st_ino == underlying_node_stat.st_ino - && stat.st_fsid == underlying_node_stat.st_fsid) - /* It's OUR root node. */ - err = ELOOP; - else if (1) //stat.st_mode & S_ITRANS) - { - port_dealloc (p); - err = file_lookup (node_ulfs->port, name, - flags, 0, 0, &p, &stat); - } - } + flags, 0, 0, &p, &stat); } } + if (! err) { *s = stat; *port = p; } + return err; } @@ -206,10 +261,14 @@ node_lookup_file (node_t *dir, char *name, int flags, void node_ulfs_free (node_t *node) { + node_ulfs_iterate_unlocked (node) - if (port_valid (node_ulfs->port) - && node_ulfs->port != underlying_node) - port_dealloc (node_ulfs->port); + { + if (port_valid (node_ulfs->port) + && node_ulfs->port != underlying_node) + port_dealloc (node_ulfs->port); + } + free (node->nn->ulfs); } @@ -223,20 +282,21 @@ node_ulfs_init (node_t *node) ulfs_new = malloc (ulfs_num * sizeof (node_ulfs_t)); if (! ulfs_new) - err = ENOMEM; - else { - if (node->nn->ulfs) - node_ulfs_free (node); + err = ENOMEM; + return err; + } - node->nn->ulfs = ulfs_new; - node->nn->ulfs_num = ulfs_num; + if (node->nn->ulfs) + node_ulfs_free (node); + + node->nn->ulfs = ulfs_new; + node->nn->ulfs_num = ulfs_num; - node_ulfs_iterate_unlocked (node) - { - node_ulfs->flags = 0; - node_ulfs->port = port_null; - } + node_ulfs_iterate_unlocked (node) + { + node_ulfs->flags = 0; + node_ulfs->port = port_null; } return err; @@ -258,8 +318,13 @@ node_entries_get (node_t *node, node_dirent_t **dirents) one. */ error_t node_dirent_add (char *name, ino_t fileno, int type) { - node_dirent_t *node_dirent; error_t e = 0; + node_dirent_t *node_dirent; + node_dirent_t *node_dirent_new; + struct dirent *dirent_new; + int name_len = strlen (name); + int size = DIRENT_LEN (name_len); + for (node_dirent = node_dirent_list; node_dirent && strcmp (node_dirent->dirent->d_name, name); @@ -271,64 +336,59 @@ node_entries_get (node_t *node, node_dirent_t **dirents) node_dirent->dirent->d_fileno = fileno; node_dirent->dirent->d_type = type; + return e; } - else + + /* Create new entry. */ + + node_dirent_new = malloc (sizeof (node_dirent_t)); + if (!node_dirent_new) + { + e = ENOMEM; + return e; + } + + dirent_new = malloc (size); + if (!dirent_new) { - /* Create new entry. */ - - node_dirent_t *node_dirent_new; - - node_dirent_new = malloc (sizeof (node_dirent_t)); - if (node_dirent_new) - { - int name_len = strlen (name); - int size = DIRENT_LEN (name_len); - struct dirent *dirent_new; - - dirent_new = malloc (size); - if (dirent_new) - { - /* Fill dirent. */ - dirent_new->d_fileno = fileno; - dirent_new->d_type = type; - dirent_new->d_reclen = size; - strcpy ((char *) dirent_new + DIRENT_NAME_OFFS, name); - - /* Add dirent to the list. */ - node_dirent_new->dirent = dirent_new; - node_dirent_new->next = node_dirent_list; - node_dirent_list = node_dirent_new; - } - else - { - free (node_dirent_new); - e = ENOMEM; - } - } - else - e = ENOMEM; + free (node_dirent_new); + e = ENOMEM; + return e; } + + /* Fill dirent. */ + dirent_new->d_fileno = fileno; + dirent_new->d_type = type; + dirent_new->d_reclen = size; + strcpy ((char *) dirent_new + DIRENT_NAME_OFFS, name); + + /* Add dirent to the list. */ + node_dirent_new->dirent = dirent_new; + node_dirent_new->next = node_dirent_list; + node_dirent_list = node_dirent_new; + return e; } node_ulfs_iterate_unlocked(node) { - if (port_valid (node_ulfs->port)) - { - err = dir_entries_get (node_ulfs->port, &dirent_data, - &dirent_data_size, &dirent_list); - if (! err) - { - for (dirent = dirent_list; (! err) && *dirent; dirent++) - if (strcmp ((*dirent)->d_name, ".") - && strcmp ((*dirent)->d_name, "..")) - err = node_dirent_add ((*dirent)->d_name, - (*dirent)->d_fileno, - (*dirent)->d_type); - free (dirent_list); - munmap (dirent_data, dirent_data_size); - } - } + if (!port_valid (node_ulfs->port)) + continue; + + err = dir_entries_get (node_ulfs->port, &dirent_data, + &dirent_data_size, &dirent_list); + if (err) + continue; + + for (dirent = dirent_list; (! err) && *dirent; dirent++) + if (strcmp ((*dirent)->d_name, ".") + && strcmp ((*dirent)->d_name, "..")) + err = node_dirent_add ((*dirent)->d_name, + (*dirent)->d_fileno, + (*dirent)->d_type); + + free (dirent_list); + munmap (dirent_data, dirent_data_size); } if (err) @@ -363,17 +423,18 @@ node_create_root (node_t **root_node) error_t err = 0; err = lnode_create (NULL, &lnode); - if (! err) + if (err) + return err; + + err = node_create (lnode, &node); + if (err) { - err = node_create (lnode, &node); - if (err) - lnode_destroy (lnode); - else - mutex_unlock (&lnode->lock); + lnode_destroy (lnode); + return err; } - if (! err) - *root_node = node; + mutex_unlock (&lnode->lock); + *root_node = node; return err; } @@ -383,36 +444,42 @@ error_t node_init_root (node_t *node) { error_t err; + ulfs_t *ulfs; + int i = 0; mutex_lock (&ulfs_lock); err = node_ulfs_init (node); - if (! err) + if (err) + { + mutex_unlock (&ulfs_lock); + return err; + } + + node_ulfs_iterate_unlocked (node) { - ulfs_t *ulfs; - int i = 0; - node_ulfs_iterate_unlocked (node) + if (err) + break; + + err = ulfs_get_num (i, &ulfs); + if (err) + break; + + if (ulfs->path) + node_ulfs->port = file_name_lookup (ulfs->path, + O_READ | O_DIRECTORY, 0); + else + node_ulfs->port = underlying_node; + + if (! port_valid (node_ulfs->port)) { - if (! err) - err = ulfs_get_num (i, &ulfs); - if (! err) - { - if (ulfs->path) - node_ulfs->port = file_name_lookup (ulfs->path, - O_READ | O_DIRECTORY, 0); - else - node_ulfs->port = underlying_node; - - if (! port_valid (node_ulfs->port)) - err = errno; - else - { - node_ulfs->flags |= FLAG_NODE_ULFS_FIXED; - i++; - } - } + err = errno; + break; } + + node_ulfs->flags |= FLAG_NODE_ULFS_FIXED; + i++; } mutex_unlock (&ulfs_lock); diff --git a/node.h b/node.h index 4d30417..e647ce0 100644 --- a/node.h +++ b/node.h @@ -80,6 +80,10 @@ void node_destroy (node_t *node); which must be locked, are uptodate. */ error_t node_update (node_t *node); +/* Remove all files named NAME beneath DIR on the underlying filesystems + with FLAGS as openflags. */ +error_t node_unlink_file (node_t *dir, char *name); + /* Lookup a file named NAME beneath DIR on the underlying filesystems with FLAGS as openflags. Return the first port successfully looked up in *PORT and according stat information in *STAT. */ @@ -115,4 +119,9 @@ error_t node_init_root (node_t *node); node_ulfs < (node)->nn->ulfs + (node)->nn->ulfs_num; \ node_ulfs++) +#define node_ulfs_iterate_reverse_unlocked(node) \ + for (node_ulfs_t *node_ulfs = (node)->nn->ulfs + (node)->nn->ulfs_num - 1;\ + node_ulfs >= (node)->nn->ulfs; \ + node_ulfs--) + #endif diff --git a/options.c b/options.c index 28a68bf..d14b3ef 100644 --- a/options.c +++ b/options.c @@ -113,8 +113,6 @@ argp_parse_common_options (int key, char *arg, struct argp_state *state) case OPT_UNDERLYING: /* --underlying */ case ARGP_KEY_ARG: - fprintf (stderr, "adding %s\n", arg); - if (ulfs_remove) { err = ulfs_unregister (arg); -- cgit v1.2.3