From 1ad1c0f26223878808e85985e327ef5c548ea020 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Fri, 15 Oct 2010 14:30:41 -0400 Subject: [PATCH] symlink: rename and link handle ERROR_REPARSE when rename or link call nfs41_lookup() for the destination directory, they need to be able to handle ERROR_REPARSE and find the real dest dir open now does the same thing when it sees ERROR_REPARSE; previously, it was only replacing the first symlink in the path, and could require multiple reparses on a path modified nfs41_symlink_target() to support the case where the source and destination paths are the same (used by rename/link) Signed-off-by: Casey Bodley --- daemon/open.c | 16 ++++++++++++---- daemon/setattr.c | 24 ++++++++++++++++++++++-- daemon/symlink.c | 28 +++++++++++++++------------- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/daemon/open.c b/daemon/open.c index 56a047c..b1f8f7f 100644 --- a/daemon/open.c +++ b/daemon/open.c @@ -238,12 +238,20 @@ int handle_open(nfs41_upcall *upcall) if (status == ERROR_REPARSE) { /* one of the parent components was a symlink */ + do { + /* replace the path with the symlink target's */ + status = nfs41_symlink_target(state->session, + &state->parent, &state->path); + + /* redo the lookup until it doesn't return REPARSE */ + status = nfs41_lookup(args->root, state->session, + &state->path, &state->parent, NULL, NULL, &state->session); + } while (status == ERROR_REPARSE); + + abs_path_copy(&args->symlink, &state->path); + status = NO_ERROR; upcall->last_error = ERROR_REPARSE; args->symlink_embedded = TRUE; - - /* replace the path with the symlink target */ - status = nfs41_symlink_target(state->session, - &state->parent, &args->symlink); goto out_free_state; } diff --git a/daemon/setattr.c b/daemon/setattr.c index 332aa2f..2df1a10 100644 --- a/daemon/setattr.c +++ b/daemon/setattr.c @@ -199,6 +199,7 @@ static void open_state_rename( static int handle_nfs41_rename(setattr_upcall_args *args) { nfs41_open_state *state = args->state; + nfs41_session *dst_session; PFILE_RENAME_INFO rename = (PFILE_RENAME_INFO)args->buf; nfs41_abs_path dst_path; nfs41_path_fh dst_dir; @@ -248,7 +249,16 @@ static int handle_nfs41_rename(setattr_upcall_args *args) /* the destination path is absolute, so start from the root session */ status = nfs41_lookup(args->root, nfs41_root_session(args->root), - &dst_path, &dst_dir, NULL, NULL, NULL); + &dst_path, &dst_dir, NULL, NULL, &dst_session); + + while (status == ERROR_REPARSE) { + /* replace the path with the symlink target's */ + status = nfs41_symlink_target(dst_session, &dst_dir, &dst_path); + + /* redo the lookup until it doesn't return REPARSE */ + status = nfs41_lookup(args->root, dst_session, + &dst_path, &dst_dir, NULL, NULL, &dst_session); + } /* get the components after lookup in case a referral changed its path */ last_component(dst_path.path, dst_path.path + dst_path.len, &dst_name); @@ -322,6 +332,7 @@ int handle_nfs41_link(setattr_upcall_args *args) { nfs41_open_state *state = args->state; PFILE_LINK_INFORMATION link = (PFILE_LINK_INFORMATION)args->buf; + nfs41_session *dst_session; nfs41_abs_path dst_path; nfs41_path_fh dst_dir; nfs41_component dst_name; @@ -342,7 +353,16 @@ int handle_nfs41_link(setattr_upcall_args *args) /* the destination path is absolute, so start from the root session */ status = nfs41_lookup(args->root, nfs41_root_session(args->root), - &dst_path, &dst_dir, NULL, NULL, NULL); + &dst_path, &dst_dir, NULL, NULL, &dst_session); + + while (status == ERROR_REPARSE) { + /* replace the path with the symlink target's */ + status = nfs41_symlink_target(dst_session, &dst_dir, &dst_path); + + /* redo the lookup until it doesn't return REPARSE */ + status = nfs41_lookup(args->root, dst_session, + &dst_path, &dst_dir, NULL, NULL, &dst_session); + } /* get the components after lookup in case a referral changed its path */ last_component(dst_path.path, dst_path.path + dst_path.len, &dst_name); diff --git a/daemon/symlink.c b/daemon/symlink.c index 1411b42..68f8426 100644 --- a/daemon/symlink.c +++ b/daemon/symlink.c @@ -113,29 +113,31 @@ int nfs41_symlink_target( dprintf(2, "--> nfs41_symlink_target('%s', '%s')\n", path->path, link); + /* append any components after the symlink */ + if (FAILED(StringCchCatA(link, NFS41_MAX_PATH_LEN, + file->name.name + file->name.len))) { + status = ERROR_BUFFER_OVERFLOW; + goto out; + } + link_len = (uint32_t)strlen(link); + /* overwrite the last component of the path; get the starting offset */ path_offset = file->name.name - path->path; /* copy the path and update it with the results from link */ - target->len = path->len; - if (FAILED(StringCchCopyNA(target->path, NFS41_MAX_PATH_LEN, - path->path, path->len))) { - status = ERROR_BUFFER_OVERFLOW; - goto out; + if (target != path) { + target->len = path->len; + if (FAILED(StringCchCopyNA(target->path, NFS41_MAX_PATH_LEN, + path->path, path->len))) { + status = ERROR_BUFFER_OVERFLOW; + goto out; + } } status = abs_path_link(target, target->path + path_offset, link, link_len); if (status) { eprintf("abs_path_link() failed with %d\n", status); goto out; } - - /* append any components after the symlink */ - if (FAILED(StringCchCopyA(target->path + target->len, - NFS41_MAX_PATH_LEN - target->len, file->name.name + file->name.len))) { - status = ERROR_BUFFER_OVERFLOW; - goto out; - } - target->len = (unsigned short)strlen(target->path); out: dprintf(2, "<-- nfs41_symlink_target('%s') returning %d\n", target->path, status);