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 <cbodley@citi.umich.edu>
This commit is contained in:
Casey Bodley 2010-10-15 14:30:41 -04:00 committed by unknown
parent e37b33a4df
commit 1ad1c0f262
3 changed files with 49 additions and 19 deletions

View file

@ -238,12 +238,20 @@ int handle_open(nfs41_upcall *upcall)
if (status == ERROR_REPARSE) { if (status == ERROR_REPARSE) {
/* one of the parent components was a symlink */ /* 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; upcall->last_error = ERROR_REPARSE;
args->symlink_embedded = TRUE; 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; goto out_free_state;
} }

View file

@ -199,6 +199,7 @@ static void open_state_rename(
static int handle_nfs41_rename(setattr_upcall_args *args) static int handle_nfs41_rename(setattr_upcall_args *args)
{ {
nfs41_open_state *state = args->state; nfs41_open_state *state = args->state;
nfs41_session *dst_session;
PFILE_RENAME_INFO rename = (PFILE_RENAME_INFO)args->buf; PFILE_RENAME_INFO rename = (PFILE_RENAME_INFO)args->buf;
nfs41_abs_path dst_path; nfs41_abs_path dst_path;
nfs41_path_fh dst_dir; 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 */ /* the destination path is absolute, so start from the root session */
status = nfs41_lookup(args->root, nfs41_root_session(args->root), 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 */ /* 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); 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; nfs41_open_state *state = args->state;
PFILE_LINK_INFORMATION link = (PFILE_LINK_INFORMATION)args->buf; PFILE_LINK_INFORMATION link = (PFILE_LINK_INFORMATION)args->buf;
nfs41_session *dst_session;
nfs41_abs_path dst_path; nfs41_abs_path dst_path;
nfs41_path_fh dst_dir; nfs41_path_fh dst_dir;
nfs41_component dst_name; 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 */ /* the destination path is absolute, so start from the root session */
status = nfs41_lookup(args->root, nfs41_root_session(args->root), 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 */ /* 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); last_component(dst_path.path, dst_path.path + dst_path.len, &dst_name);

View file

@ -113,29 +113,31 @@ int nfs41_symlink_target(
dprintf(2, "--> nfs41_symlink_target('%s', '%s')\n", path->path, link); 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 */ /* overwrite the last component of the path; get the starting offset */
path_offset = file->name.name - path->path; path_offset = file->name.name - path->path;
/* copy the path and update it with the results from link */ /* copy the path and update it with the results from link */
target->len = path->len; if (target != path) {
if (FAILED(StringCchCopyNA(target->path, NFS41_MAX_PATH_LEN, target->len = path->len;
path->path, path->len))) { if (FAILED(StringCchCopyNA(target->path, NFS41_MAX_PATH_LEN,
status = ERROR_BUFFER_OVERFLOW; path->path, path->len))) {
goto out; status = ERROR_BUFFER_OVERFLOW;
goto out;
}
} }
status = abs_path_link(target, target->path + path_offset, link, link_len); status = abs_path_link(target, target->path + path_offset, link, link_len);
if (status) { if (status) {
eprintf("abs_path_link() failed with %d\n", status); eprintf("abs_path_link() failed with %d\n", status);
goto out; 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: out:
dprintf(2, "<-- nfs41_symlink_target('%s') returning %d\n", dprintf(2, "<-- nfs41_symlink_target('%s') returning %d\n",
target->path, status); target->path, status);