2010-09-23 12:32:56 -04:00
|
|
|
/* Copyright (c) 2010
|
|
|
|
|
* The Regents of the University of Michigan
|
|
|
|
|
* All Rights Reserved
|
|
|
|
|
*
|
2010-10-14 13:23:48 -04:00
|
|
|
* Permission is granted to use, copy and redistribute this software
|
|
|
|
|
* for noncommercial education and research purposes, so long as no
|
|
|
|
|
* fee is charged, and so long as the name of the University of Michigan
|
|
|
|
|
* is not used in any advertising or publicity pertaining to the use
|
|
|
|
|
* or distribution of this software without specific, written prior
|
|
|
|
|
* authorization. Permission to modify or otherwise create derivative
|
|
|
|
|
* works of this software is not granted.
|
2010-09-23 12:32:56 -04:00
|
|
|
*
|
|
|
|
|
* This software is provided as is, without representation or warranty
|
|
|
|
|
* of any kind either express or implied, including without limitation
|
|
|
|
|
* the implied warranties of merchantability, fitness for a particular
|
|
|
|
|
* purpose, or noninfringement. The Regents of the University of
|
|
|
|
|
* Michigan shall not be liable for any damages, including special,
|
|
|
|
|
* indirect, incidental, or consequential damages, with respect to any
|
|
|
|
|
* claim arising out of or in connection with the use of the software,
|
|
|
|
|
* even if it has been or is hereafter advised of the possibility of
|
|
|
|
|
* such damages.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <Windows.h>
|
|
|
|
|
#include <strsafe.h>
|
|
|
|
|
|
2010-09-23 12:48:02 -04:00
|
|
|
#include "nfs41_ops.h"
|
2010-09-23 12:32:56 -04:00
|
|
|
#include "upcall.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "daemon_debug.h"
|
|
|
|
|
|
|
|
|
|
|
2010-09-24 14:12:37 -04:00
|
|
|
static int abs_path_link(
|
2010-09-24 14:03:42 -04:00
|
|
|
OUT nfs41_abs_path *path,
|
|
|
|
|
IN char *path_pos,
|
|
|
|
|
IN const char *link,
|
|
|
|
|
IN uint32_t link_len)
|
|
|
|
|
{
|
|
|
|
|
nfs41_component name;
|
|
|
|
|
const char *path_max = path->path + NFS41_MAX_PATH_LEN;
|
|
|
|
|
const char *link_pos = link;
|
|
|
|
|
const char *link_end = link + link_len;
|
|
|
|
|
int status = NO_ERROR;
|
|
|
|
|
|
|
|
|
|
/* if link is an absolute path, start path_pos at the beginning */
|
|
|
|
|
if (is_delimiter(*link))
|
|
|
|
|
path_pos = path->path;
|
|
|
|
|
|
|
|
|
|
/* copy each component of link into the path */
|
|
|
|
|
while (next_component(link_pos, link_end, &name)) {
|
|
|
|
|
link_pos = name.name + name.len;
|
|
|
|
|
|
2010-09-24 14:12:37 -04:00
|
|
|
if (is_delimiter(*path_pos))
|
|
|
|
|
path_pos++;
|
|
|
|
|
|
2010-09-24 14:03:42 -04:00
|
|
|
/* handle special components . and .. */
|
|
|
|
|
if (name.len == 1 && name.name[0] == '.')
|
|
|
|
|
continue;
|
|
|
|
|
if (name.len == 2 && name.name[0] == '.' && name.name[1] == '.') {
|
|
|
|
|
/* back path_pos up by one component */
|
|
|
|
|
if (!last_component(path->path, path_pos, &name)) {
|
|
|
|
|
eprintf("symlink with .. that points below server root!\n");
|
|
|
|
|
status = ERROR_BAD_NETPATH;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
path_pos = (char*)prev_delimiter(name.name, path->path);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 14:12:37 -04:00
|
|
|
/* copy the component and add a \ */
|
2010-09-24 14:03:42 -04:00
|
|
|
if (FAILED(StringCchCopyNA(path_pos,
|
2010-09-24 14:12:37 -04:00
|
|
|
path_max-path_pos, name.name, name.len))) {
|
2010-09-24 14:03:42 -04:00
|
|
|
status = ERROR_BUFFER_OVERFLOW;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2010-09-24 14:12:37 -04:00
|
|
|
path_pos += name.len;
|
2010-09-24 14:03:42 -04:00
|
|
|
if (FAILED(StringCchCopyNA(path_pos,
|
2010-09-24 14:12:37 -04:00
|
|
|
path_max-path_pos, "\\", 1))) {
|
2010-09-24 14:03:42 -04:00
|
|
|
status = ERROR_BUFFER_OVERFLOW;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* make sure the path is null terminated */
|
|
|
|
|
if (path_pos == path_max) {
|
|
|
|
|
status = ERROR_BUFFER_OVERFLOW;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
*path_pos = '\0';
|
|
|
|
|
out:
|
|
|
|
|
path->len = (unsigned short)(path_pos - path->path);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-14 15:20:45 -04:00
|
|
|
int nfs41_symlink_target(
|
2010-09-24 14:12:37 -04:00
|
|
|
IN nfs41_session *session,
|
|
|
|
|
IN nfs41_path_fh *file,
|
|
|
|
|
OUT nfs41_abs_path *target)
|
2010-09-24 14:03:42 -04:00
|
|
|
{
|
|
|
|
|
char link[NFS41_MAX_PATH_LEN];
|
2010-09-24 14:12:37 -04:00
|
|
|
const nfs41_abs_path *path = file->path;
|
2010-09-24 14:03:42 -04:00
|
|
|
ptrdiff_t path_offset;
|
|
|
|
|
uint32_t link_len;
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
/* read the link */
|
2010-09-24 14:12:37 -04:00
|
|
|
status = nfs41_readlink(session, file, NFS41_MAX_PATH_LEN, link, &link_len);
|
2010-09-24 14:03:42 -04:00
|
|
|
if (status) {
|
2010-09-24 14:12:37 -04:00
|
|
|
eprintf("nfs41_readlink() failed with %s\n", nfs_error_string(status));
|
2010-09-24 14:03:42 -04:00
|
|
|
status = ERROR_PATH_NOT_FOUND;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-14 15:20:45 -04:00
|
|
|
dprintf(2, "--> nfs41_symlink_target('%s', '%s')\n", path->path, link);
|
|
|
|
|
|
2010-10-15 14:30:41 -04:00
|
|
|
/* 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);
|
|
|
|
|
|
2010-09-24 14:03:42 -04:00
|
|
|
/* overwrite the last component of the path; get the starting offset */
|
2010-09-24 14:12:37 -04:00
|
|
|
path_offset = file->name.name - path->path;
|
2010-09-24 14:03:42 -04:00
|
|
|
|
|
|
|
|
/* copy the path and update it with the results from link */
|
2010-10-15 14:30:41 -04:00
|
|
|
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;
|
|
|
|
|
}
|
2010-09-24 14:03:42 -04:00
|
|
|
}
|
2010-09-24 14:12:37 -04:00
|
|
|
status = abs_path_link(target, target->path + path_offset, link, link_len);
|
2010-09-24 14:03:42 -04:00
|
|
|
if (status) {
|
|
|
|
|
eprintf("abs_path_link() failed with %d\n", status);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
out:
|
2010-10-14 15:20:45 -04:00
|
|
|
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;
|
2010-10-15 16:08:16 -04:00
|
|
|
uint32_t depth = 0;
|
2010-10-14 15:20:45 -04:00
|
|
|
int status = NO_ERROR;
|
|
|
|
|
|
|
|
|
|
file.path = &path;
|
|
|
|
|
InitializeSRWLock(&path.lock);
|
|
|
|
|
|
|
|
|
|
dprintf(2, "--> nfs41_symlink_follow('%s')\n", symlink->path->path);
|
|
|
|
|
|
|
|
|
|
do {
|
2010-10-15 16:08:16 -04:00
|
|
|
if (++depth > NFS41_MAX_SYMLINK_DEPTH) {
|
|
|
|
|
status = ERROR_TOO_MANY_LINKS;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-14 15:20:45 -04:00
|
|
|
/* 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);
|
2010-09-24 14:03:42 -04:00
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-09-23 12:32:56 -04:00
|
|
|
/* NFS41_SYMLINK */
|
|
|
|
|
int parse_symlink(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
|
|
|
|
{
|
|
|
|
|
symlink_upcall_args *args = &upcall->args.symlink;
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
|
|
|
|
|
if (status) goto out;
|
|
|
|
|
status = safe_read(&buffer, &length, &args->state, sizeof(nfs41_open_state *));
|
|
|
|
|
if (status) goto out;
|
|
|
|
|
status = get_name(&buffer, &length, &args->path);
|
|
|
|
|
if (status) goto out;
|
|
|
|
|
status = safe_read(&buffer, &length, &args->set, sizeof(BOOLEAN));
|
|
|
|
|
if (status) goto out;
|
|
|
|
|
|
|
|
|
|
if (args->set)
|
|
|
|
|
status = get_name(&buffer, &length, &args->target_set);
|
|
|
|
|
else
|
|
|
|
|
args->target_set = NULL;
|
|
|
|
|
|
|
|
|
|
dprintf(1, "parsing NFS41_SYMLINK: root=0x%p open_state=0x%p "
|
|
|
|
|
"path='%s' set=%u target='%s'\n", args->root, args->state,
|
|
|
|
|
args->path, args->set, args->target_set);
|
|
|
|
|
out:
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-23 12:48:02 -04:00
|
|
|
static int map_symlink_errors(int status)
|
|
|
|
|
{
|
|
|
|
|
switch (status) {
|
|
|
|
|
case NFS4ERR_BADCHAR:
|
|
|
|
|
case NFS4ERR_BADNAME: return ERROR_INVALID_REPARSE_DATA;
|
|
|
|
|
case NFS4ERR_WRONG_TYPE: return ERROR_NOT_A_REPARSE_POINT;
|
|
|
|
|
default: return nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-23 12:32:56 -04:00
|
|
|
int handle_symlink(nfs41_upcall *upcall)
|
|
|
|
|
{
|
2010-09-23 12:48:02 -04:00
|
|
|
symlink_upcall_args *args = &upcall->args.symlink;
|
|
|
|
|
nfs41_open_state *state = args->state;
|
|
|
|
|
int status = NO_ERROR;
|
|
|
|
|
|
|
|
|
|
if (args->set) {
|
2010-10-15 14:49:43 -04:00
|
|
|
/* don't send windows slashes to the server */
|
|
|
|
|
char *p;
|
|
|
|
|
for (p = args->target_set; *p; p++) if (*p == '\\') *p = '/';
|
|
|
|
|
|
2010-09-23 12:59:26 -04:00
|
|
|
if (state->file.fh.len) {
|
|
|
|
|
/* the check in handle_open() didn't catch that we're creating
|
|
|
|
|
* a symlink, so we have to remove the file it already created */
|
|
|
|
|
nfs41_remove(state->session, &state->parent, &state->file.name);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-23 13:13:18 -04:00
|
|
|
/* create the symlink */
|
|
|
|
|
status = nfs41_create(state->session, NF4LNK, 0777,
|
|
|
|
|
args->target_set, &state->parent, &state->file);
|
|
|
|
|
if (status) {
|
|
|
|
|
eprintf("nfs41_create() failed with %s\n",
|
|
|
|
|
nfs_error_string(status));
|
|
|
|
|
status = map_symlink_errors(status);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2010-09-23 12:48:02 -04:00
|
|
|
} else {
|
|
|
|
|
uint32_t len;
|
|
|
|
|
|
|
|
|
|
/* read the link */
|
|
|
|
|
status = nfs41_readlink(state->session, &state->file,
|
|
|
|
|
NFS41_MAX_PATH_LEN, args->target_get.path, &len);
|
|
|
|
|
if (status) {
|
|
|
|
|
eprintf("nfs41_readlink() failed with %s\n",
|
|
|
|
|
nfs_error_string(status));
|
|
|
|
|
status = map_symlink_errors(status);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
args->target_get.len = (unsigned short)len;
|
|
|
|
|
dprintf(2, "returning symlink target '%s'\n", args->target_get.path);
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
return status;
|
2010-09-23 12:32:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int marshall_symlink(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
|
|
|
|
|
{
|
|
|
|
|
symlink_upcall_args *args = &upcall->args.symlink;
|
|
|
|
|
unsigned short len = (args->target_get.len + 1) * sizeof(WCHAR);
|
|
|
|
|
int status = NO_ERROR;
|
|
|
|
|
|
|
|
|
|
if (args->set)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
status = safe_write(&buffer, length, &len, sizeof(len));
|
|
|
|
|
if (status) goto out;
|
|
|
|
|
|
|
|
|
|
if (*length <= len || !MultiByteToWideChar(CP_UTF8, 0,
|
|
|
|
|
args->target_get.path, args->target_get.len,
|
|
|
|
|
(LPWSTR)buffer, len / sizeof(WCHAR))) {
|
|
|
|
|
status = ERROR_BUFFER_OVERFLOW;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
return status;
|
|
|
|
|
}
|