diff --git a/daemon/getattr.c b/daemon/getattr.c index b528ec4..d0037e6 100644 --- a/daemon/getattr.c +++ b/daemon/getattr.c @@ -93,6 +93,14 @@ int handle_getattr(nfs41_upcall *upcall) goto out; } + if (info.type == NF4LNK) { + nfs41_file_info target_info; + int target_status = nfs41_symlink_follow(args->root, + state->session, &state->file, &target_info); + if (target_status == NO_ERROR && target_info.type == NF4DIR) + info.symlink_dir = TRUE; + } + switch (args->query_class) { case FileBasicInformation: nfs_to_basic_info(&info, &args->basic_info); diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h index 12a5169..0eec2d1 100644 --- a/daemon/nfs41_ops.h +++ b/daemon/nfs41_ops.h @@ -1022,11 +1022,17 @@ int nfs41_link( OUT OPTIONAL nfs41_path_fh *link_out); /* symlink.c */ -int nfs41_symlink_follow( +int nfs41_symlink_target( IN nfs41_session *session, IN nfs41_path_fh *file, OUT nfs41_abs_path *target); +int nfs41_symlink_follow( + IN nfs41_root *root, + IN nfs41_session *session, + IN nfs41_path_fh *symlink, + OUT nfs41_file_info *info); + int nfs41_readlink( IN nfs41_session *session, IN nfs41_path_fh *file, diff --git a/daemon/nfs41_types.h b/daemon/nfs41_types.h index 3ce9bd7..8684722 100644 --- a/daemon/nfs41_types.h +++ b/daemon/nfs41_types.h @@ -184,6 +184,7 @@ typedef struct __nfs41_file_info { bool_t cansettime; /* XXX: per-fs */ bool_t case_insensitive; bool_t case_preserving; + bool_t symlink_dir; } nfs41_file_info; #endif /* !__NFS41_DAEMON_TYPES_H__ */ diff --git a/daemon/open.c b/daemon/open.c index 8fcba8c..56a047c 100644 --- a/daemon/open.c +++ b/daemon/open.c @@ -217,7 +217,7 @@ int handle_open(nfs41_upcall *upcall) int status = 0; open_upcall_args *args = &upcall->args.open; nfs41_open_state *state; - nfs41_file_info info; + nfs41_file_info info = { 0 }; status = create_open_state(args->path, args->open_owner_id, &state); if (status) { @@ -242,7 +242,7 @@ int handle_open(nfs41_upcall *upcall) args->symlink_embedded = TRUE; /* replace the path with the symlink target */ - status = nfs41_symlink_follow(state->session, + status = nfs41_symlink_target(state->session, &state->parent, &args->symlink); goto out_free_state; } @@ -270,14 +270,20 @@ int handle_open(nfs41_upcall *upcall) } else if (info.type == NF4LNK) { dprintf(2, "handle nfs41_open: SYMLINK\n"); if (args->create_opts & FILE_OPEN_REPARSE_POINT) { - /* continue and open the symlink itself */ + /* continue and open the symlink itself, but we need to + * know if the target is a regular file or directory */ + nfs41_file_info target_info; + int target_status = nfs41_symlink_follow(args->root, + state->session, &state->file, &target_info); + if (target_status == NO_ERROR && target_info.type == NF4DIR) + info.symlink_dir = TRUE; } else { /* tell the driver to call RxPrepareToReparseSymbolicLink() */ upcall->last_error = ERROR_REPARSE; args->symlink_embedded = FALSE; /* replace the path with the symlink target */ - status = nfs41_symlink_follow(state->session, + status = nfs41_symlink_target(state->session, &state->file, &args->symlink); goto out_free_state; } @@ -286,7 +292,7 @@ int handle_open(nfs41_upcall *upcall) state->type = info.type; } else if (status != ERROR_FILE_NOT_FOUND) goto out_free_state; - + /* XXX: this is a hard-coded check for the open arguments we see from * the CreateSymbolicLink() system call. we respond to this by deferring * the CREATE until we get the upcall to set the symlink. this approach diff --git a/daemon/readdir.c b/daemon/readdir.c index a2ccec6..2e44bd0 100644 --- a/daemon/readdir.c +++ b/daemon/readdir.c @@ -170,10 +170,6 @@ static void readdir_copy_dir_info( entry->attr_info.size; info->fdi.FileAttributes = nfs_file_info_to_attributes( &entry->attr_info); - - /* use cansettime to flag the symlink target as a directory */ - if (entry->attr_info.cansettime) - info->fdi.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } static void readdir_copy_shortname( @@ -249,7 +245,7 @@ static int lookup_entry( int status; name.name = entry->name; - name.len = (unsigned short)entry->name_len; + name.len = (unsigned short)entry->name_len - 1; status = format_abs_path(parent->path, &name, &path); if (status) goto out; @@ -269,7 +265,7 @@ static int lookup_symlink( OUT nfs41_file_info *info_out) { nfs41_abs_path path; - nfs41_path_fh link_parent, file; + nfs41_path_fh file; nfs41_file_info info; int status; @@ -277,28 +273,15 @@ static int lookup_symlink( if (status) goto out; file.path = &path; - /* get a filehandle for the symlink */ - status = nfs41_lookup(root, session, &path, NULL, &file, &info, NULL); + status = nfs41_lookup(root, session, &path, NULL, &file, &info, &session); if (status) goto out; - link_parent.path = &path; last_component(path.path, path.path + path.len, &file.name); - last_component(path.path, file.name.name, &link_parent.name); - /* attempt to follow the symlink */ - status = nfs41_symlink_follow(session, &file, &path); + status = nfs41_symlink_follow(root, session, &file, &info); if (status) goto out; - /* get attributes for the target */ - status = nfs41_lookup(root, session, &path, &link_parent, NULL, &info, NULL); - if (status) goto out; - - if (info.type == NF4LNK) { - status = lookup_symlink(root, session, &link_parent, &file.name, &info); - if (status) goto out; - info_out->cansettime = info.cansettime; - } else if (info.type == NF4DIR) - info_out->cansettime = 1; + info_out->symlink_dir = info.type == NF4DIR; out: return status; } diff --git a/daemon/symlink.c b/daemon/symlink.c index 770b286..1411b42 100644 --- a/daemon/symlink.c +++ b/daemon/symlink.c @@ -42,8 +42,6 @@ static int abs_path_link( const char *link_end = link + link_len; int status = NO_ERROR; - dprintf(2, "--> abs_path_link('%s', '%s')\n", path->path, link); - /* if link is an absolute path, start path_pos at the beginning */ if (is_delimiter(*link)) path_pos = path->path; @@ -91,11 +89,10 @@ static int abs_path_link( *path_pos = '\0'; out: path->len = (unsigned short)(path_pos - path->path); - dprintf(2, "<-- abs_path_link('%s') returning %d\n", path->path, status); return status; } -int nfs41_symlink_follow( +int nfs41_symlink_target( IN nfs41_session *session, IN nfs41_path_fh *file, OUT nfs41_abs_path *target) @@ -114,6 +111,8 @@ int nfs41_symlink_follow( goto out; } + dprintf(2, "--> nfs41_symlink_target('%s', '%s')\n", path->path, link); + /* overwrite the last component of the path; get the starting offset */ path_offset = file->name.name - path->path; @@ -127,7 +126,6 @@ int nfs41_symlink_follow( status = abs_path_link(target, target->path + path_offset, link, link_len); if (status) { eprintf("abs_path_link() failed with %d\n", status); - status = ERROR_PATH_NOT_FOUND; goto out; } @@ -139,6 +137,44 @@ int nfs41_symlink_follow( } target->len = (unsigned short)strlen(target->path); out: + dprintf(2, "<-- nfs41_symlink_target('%s') returning %d\n", + target->path, status); + return status; +} + +int nfs41_symlink_follow( + IN nfs41_root *root, + IN nfs41_session *session, + IN nfs41_path_fh *symlink, + OUT nfs41_file_info *info) +{ + nfs41_abs_path path; + nfs41_path_fh file; + int status = NO_ERROR; + + file.path = &path; + InitializeSRWLock(&path.lock); + + dprintf(2, "--> nfs41_symlink_follow('%s')\n", symlink->path->path); + + do { + /* construct the target path */ + status = nfs41_symlink_target(session, symlink, &path); + if (status) goto out; + + dprintf(2, "looking up '%s'\n", path.path); + + last_component(path.path, path.path + path.len, &file.name); + + /* get attributes for the target */ + status = nfs41_lookup(root, session, &path, + NULL, &file, info, &session); + if (status) goto out; + + symlink = &file; + } while (info->type == NF4LNK); +out: + dprintf(2, "<-- nfs41_symlink_follow() returning %d\n", status); return status; } diff --git a/daemon/util.c b/daemon/util.c index 6a3f207..32d232f 100644 --- a/daemon/util.c +++ b/daemon/util.c @@ -130,9 +130,11 @@ ULONG nfs_file_info_to_attributes( ULONG attrs = 0; if (info->type == NF4DIR) attrs |= FILE_ATTRIBUTE_DIRECTORY; - else if (info->type == NF4LNK) + else if (info->type == NF4LNK) { attrs |= FILE_ATTRIBUTE_REPARSE_POINT; - else if (info->type != NF4REG) + if (info->symlink_dir) + attrs |= FILE_ATTRIBUTE_DIRECTORY; + } else if (info->type != NF4REG) dprintf(1, "unhandled file type %d, defaulting to NF4REG\n", info->type); @@ -162,11 +164,13 @@ void nfs_to_standard_info( IN const nfs41_file_info *info, OUT PFILE_STANDARD_INFO std_out) { + const ULONG FileAttributes = nfs_file_info_to_attributes(info); + std_out->AllocationSize.QuadPart = std_out->EndOfFile.QuadPart = (LONGLONG)info->size; std_out->NumberOfLinks = info->numlinks; std_out->DeletePending = FALSE; - std_out->Directory = info->type == NF4DIR ? TRUE : FALSE; + std_out->Directory = FileAttributes & FILE_ATTRIBUTE_DIRECTORY; }