symlink: create cygwin symlinks
when given a NfsSymlinkTargetName EA on create, marshall the unicode target name to the daemon, which issues a CREATE rpc to create the symlink modified the driver's marshall_unicode_as_utf8() function to handle the NULL string buffer for non-symlink opens Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
parent
453c0cc875
commit
0afc5ebb4d
5 changed files with 83 additions and 20 deletions
|
|
@ -256,6 +256,23 @@ static int open_or_delegate(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int parse_abs_path(unsigned char **buffer, uint32_t *length, nfs41_abs_path *path)
|
||||||
|
{
|
||||||
|
int status = safe_read(buffer, length, &path->len, sizeof(USHORT));
|
||||||
|
if (status) goto out;
|
||||||
|
if (path->len == 0)
|
||||||
|
goto out;
|
||||||
|
if (path->len >= NFS41_MAX_PATH_LEN) {
|
||||||
|
status = ERROR_BUFFER_OVERFLOW;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
status = safe_read(buffer, length, path->path, path->len);
|
||||||
|
if (status) goto out;
|
||||||
|
path->len--; /* subtract 1 for null */
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/* NFS41_OPEN */
|
/* NFS41_OPEN */
|
||||||
static int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
static int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
|
||||||
{
|
{
|
||||||
|
|
@ -280,15 +297,18 @@ static int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upca
|
||||||
if (status) goto out;
|
if (status) goto out;
|
||||||
status = safe_read(&buffer, &length, &args->srv_open, sizeof(HANDLE));
|
status = safe_read(&buffer, &length, &args->srv_open, sizeof(HANDLE));
|
||||||
if (status) goto out;
|
if (status) goto out;
|
||||||
|
status = parse_abs_path(&buffer, &length, &args->symlink);
|
||||||
|
if (status) goto out;
|
||||||
status = safe_read(&buffer, &length, &args->ea, sizeof(HANDLE));
|
status = safe_read(&buffer, &length, &args->ea, sizeof(HANDLE));
|
||||||
if (status) goto out;
|
if (status) goto out;
|
||||||
|
|
||||||
dprintf(1, "parsing NFS41_OPEN: filename='%s' access mask=%d "
|
dprintf(1, "parsing NFS41_OPEN: filename='%s' access mask=%d "
|
||||||
"access mode=%d\n\tfile attrs=0x%x create attrs=0x%x "
|
"access mode=%d\n\tfile attrs=0x%x create attrs=0x%x "
|
||||||
"(kernel) disposition=%d\n\topen_owner_id=%d mode=%o "
|
"(kernel) disposition=%d\n\topen_owner_id=%d mode=%o "
|
||||||
"srv_open=%p ea=%p\n", args->path, args->access_mask, args->access_mode,
|
"srv_open=%p symlink=%s ea=%p\n", args->path, args->access_mask,
|
||||||
args->file_attrs, args->create_opts, args->disposition,
|
args->access_mode, args->file_attrs, args->create_opts,
|
||||||
args->open_owner_id, args->mode, args->srv_open, args->ea);
|
args->disposition, args->open_owner_id, args->mode, args->srv_open,
|
||||||
|
args->symlink.path, args->ea);
|
||||||
print_disposition(2, args->disposition);
|
print_disposition(2, args->disposition);
|
||||||
print_access_mask(2, args->access_mask);
|
print_access_mask(2, args->access_mask);
|
||||||
print_share_mode(2, args->access_mode);
|
print_share_mode(2, args->access_mode);
|
||||||
|
|
@ -590,6 +610,29 @@ static int handle_open(nfs41_upcall *upcall)
|
||||||
state->file.fh.superblock = state->parent.fh.superblock;
|
state->file.fh.superblock = state->parent.fh.superblock;
|
||||||
|
|
||||||
status = NO_ERROR;
|
status = NO_ERROR;
|
||||||
|
} else if (args->symlink.len) {
|
||||||
|
/* handle cygwin symlinks */
|
||||||
|
nfs41_file_info createattrs;
|
||||||
|
createattrs.attrmask.count = 2;
|
||||||
|
createattrs.attrmask.arr[0] = 0;
|
||||||
|
createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
|
||||||
|
createattrs.mode = 0777;
|
||||||
|
|
||||||
|
dprintf(1, "creating cygwin symlink %s -> %s\n",
|
||||||
|
state->file.name.name, args->symlink.path);
|
||||||
|
|
||||||
|
status = nfs41_create(state->session, NF4LNK, &createattrs,
|
||||||
|
args->symlink.path, &state->parent, &state->file, &info);
|
||||||
|
if (status) {
|
||||||
|
eprintf("nfs41_create() for symlink=%s failed with %s\n",
|
||||||
|
args->symlink.path, nfs_error_string(status));
|
||||||
|
status = map_symlink_errors(status);
|
||||||
|
goto out_free_state;
|
||||||
|
}
|
||||||
|
nfs_to_basic_info(&info, &args->basic_info);
|
||||||
|
nfs_to_standard_info(&info, &args->std_info);
|
||||||
|
args->mode = info.mode;
|
||||||
|
args->changeattr = info.change;
|
||||||
} else if (open_for_attributes(state->type, args->access_mask,
|
} else if (open_for_attributes(state->type, args->access_mask,
|
||||||
args->disposition, status)) {
|
args->disposition, status)) {
|
||||||
if (status) {
|
if (status) {
|
||||||
|
|
|
||||||
|
|
@ -208,18 +208,6 @@ out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
case NFS4ERR_ACCESS: return ERROR_ACCESS_DENIED;
|
|
||||||
case NFS4ERR_NOTEMPTY: return ERROR_NOT_EMPTY;
|
|
||||||
default: return nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_symlink(nfs41_upcall *upcall)
|
static int handle_symlink(nfs41_upcall *upcall)
|
||||||
{
|
{
|
||||||
symlink_upcall_args *args = &upcall->args.symlink;
|
symlink_upcall_args *args = &upcall->args.symlink;
|
||||||
|
|
@ -250,7 +238,7 @@ static int handle_symlink(nfs41_upcall *upcall)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create the symlink */
|
/* create the symlink */
|
||||||
createattrs.attrmask.count = 1;
|
createattrs.attrmask.count = 2;
|
||||||
createattrs.attrmask.arr[0] = 0;
|
createattrs.attrmask.arr[0] = 0;
|
||||||
createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
|
createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
|
||||||
createattrs.mode = 0777;
|
createattrs.mode = 0777;
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,18 @@ int nfs_to_windows_error(int status, int default_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
case NFS4ERR_ACCESS: return ERROR_ACCESS_DENIED;
|
||||||
|
case NFS4ERR_NOTEMPTY: return ERROR_NOT_EMPTY;
|
||||||
|
default: return nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool_t next_component(
|
bool_t next_component(
|
||||||
IN const char *path,
|
IN const char *path,
|
||||||
IN const char *path_end,
|
IN const char *path_end,
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,7 @@ bool_t multi_addr_find(
|
||||||
*/
|
*/
|
||||||
int nfs_to_windows_error(int status, int default_error);
|
int nfs_to_windows_error(int status, int default_error);
|
||||||
|
|
||||||
|
int map_symlink_errors(int status);
|
||||||
|
|
||||||
__inline uint32_t align8(uint32_t offset) {
|
__inline uint32_t align8(uint32_t offset) {
|
||||||
return 8 + ((offset - 1) & ~7);
|
return 8 + ((offset - 1) & ~7);
|
||||||
|
|
|
||||||
|
|
@ -527,6 +527,13 @@ NTSTATUS marshall_unicode_as_utf8(
|
||||||
ULONG ActualCount;
|
ULONG ActualCount;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
|
||||||
|
if (str->Length == 0) {
|
||||||
|
status = STATUS_SUCCESS;
|
||||||
|
ActualCount = 0;
|
||||||
|
ansi.MaximumLength = 1;
|
||||||
|
goto out_copy;
|
||||||
|
}
|
||||||
|
|
||||||
/* query the number of bytes required for the utf8 encoding */
|
/* query the number of bytes required for the utf8 encoding */
|
||||||
status = RtlUnicodeToUTF8N(NULL, 0xffff,
|
status = RtlUnicodeToUTF8N(NULL, 0xffff,
|
||||||
&ActualCount, str->Buffer, str->Length);
|
&ActualCount, str->Buffer, str->Length);
|
||||||
|
|
@ -547,6 +554,7 @@ NTSTATUS marshall_unicode_as_utf8(
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_copy:
|
||||||
RtlCopyMemory(*pos, &ansi.MaximumLength, sizeof(ansi.MaximumLength));
|
RtlCopyMemory(*pos, &ansi.MaximumLength, sizeof(ansi.MaximumLength));
|
||||||
*pos += sizeof(ansi.MaximumLength);
|
*pos += sizeof(ansi.MaximumLength);
|
||||||
(*pos)[ActualCount] = '\0';
|
(*pos)[ActualCount] = '\0';
|
||||||
|
|
@ -683,7 +691,8 @@ NTSTATUS marshal_nfs41_open(
|
||||||
else
|
else
|
||||||
tmp += *len;
|
tmp += *len;
|
||||||
header_len = *len + length_as_utf8(entry->u.Open.filename) +
|
header_len = *len + length_as_utf8(entry->u.Open.filename) +
|
||||||
5 * sizeof(ULONG) + sizeof(LONG) + sizeof(DWORD) + 2 * sizeof(HANDLE);
|
7 * sizeof(ULONG) + 2 * sizeof(HANDLE) +
|
||||||
|
length_as_utf8(&entry->u.Open.symlink);
|
||||||
if (header_len > buf_len) {
|
if (header_len > buf_len) {
|
||||||
status = STATUS_INSUFFICIENT_RESOURCES;
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -709,6 +718,8 @@ NTSTATUS marshal_nfs41_open(
|
||||||
tmp += sizeof(DWORD);
|
tmp += sizeof(DWORD);
|
||||||
RtlCopyMemory(tmp, &entry->u.Open.srv_open, sizeof(HANDLE));
|
RtlCopyMemory(tmp, &entry->u.Open.srv_open, sizeof(HANDLE));
|
||||||
tmp += sizeof(HANDLE);
|
tmp += sizeof(HANDLE);
|
||||||
|
status = marshall_unicode_as_utf8(&tmp, &entry->u.Open.symlink);
|
||||||
|
if (status) goto out;
|
||||||
|
|
||||||
__try {
|
__try {
|
||||||
if (entry->u.Open.EaMdl) {
|
if (entry->u.Open.EaMdl) {
|
||||||
|
|
@ -3408,6 +3419,7 @@ NTSTATUS map_open_errors(
|
||||||
case ERROR_ACCESS_DENIED:
|
case ERROR_ACCESS_DENIED:
|
||||||
if (len > 0) return STATUS_ACCESS_DENIED;
|
if (len > 0) return STATUS_ACCESS_DENIED;
|
||||||
else return STATUS_SUCCESS;
|
else return STATUS_SUCCESS;
|
||||||
|
case ERROR_INVALID_REPARSE_DATA:
|
||||||
case ERROR_INVALID_NAME: return STATUS_OBJECT_NAME_INVALID;
|
case ERROR_INVALID_NAME: return STATUS_OBJECT_NAME_INVALID;
|
||||||
case ERROR_FILE_EXISTS: return STATUS_OBJECT_NAME_COLLISION;
|
case ERROR_FILE_EXISTS: return STATUS_OBJECT_NAME_COLLISION;
|
||||||
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
|
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
|
||||||
|
|
@ -3455,7 +3467,7 @@ static BOOLEAN create_should_pass_ea(
|
||||||
IN PFILE_FULL_EA_INFORMATION ea,
|
IN PFILE_FULL_EA_INFORMATION ea,
|
||||||
IN ULONG disposition)
|
IN ULONG disposition)
|
||||||
{
|
{
|
||||||
/* don't pass cygwin EAs (until we support NfsSymlinkTargetName) */
|
/* don't pass cygwin EAs */
|
||||||
if (AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)
|
if (AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)
|
||||||
|| AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength)
|
|| AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength)
|
||||||
|| AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength))
|
|| AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength))
|
||||||
|
|
@ -3654,7 +3666,12 @@ NTSTATUS nfs41_Create(
|
||||||
if (params->FileAttributes & FILE_ATTRIBUTE_READONLY)
|
if (params->FileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||||
entry->u.Open.mode = 0444;
|
entry->u.Open.mode = 0444;
|
||||||
}
|
}
|
||||||
|
if (entry->u.Open.disp == FILE_CREATE && ea &&
|
||||||
|
AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
|
||||||
|
/* for a cygwin symlink, given as a unicode string */
|
||||||
|
entry->u.Open.symlink.Buffer = (PWCH)(ea->EaName + ea->EaNameLength + 1);
|
||||||
|
entry->u.Open.symlink.MaximumLength = entry->u.Open.symlink.Length = ea->EaValueLength;
|
||||||
|
}
|
||||||
if (ea && create_should_pass_ea(ea, params->Disposition)) {
|
if (ea && create_should_pass_ea(ea, params->Disposition)) {
|
||||||
/* lock the extended attribute buffer for read access in user space */
|
/* lock the extended attribute buffer for read access in user space */
|
||||||
entry->u.Open.EaMdl = IoAllocateMdl(ea,
|
entry->u.Open.EaMdl = IoAllocateMdl(ea,
|
||||||
|
|
@ -6353,10 +6370,12 @@ NTSTATUS check_nfs41_getreparse_args(
|
||||||
status = STATUS_INVALID_PARAMETER;
|
status = STATUS_INVALID_PARAMETER;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
/* ifs reparse tests expect STATUS_INVALID_PARAMETER,
|
||||||
|
* but 'dir' passes a buffer here when querying symlinks
|
||||||
if (FsCtl->pInputBuffer != NULL) {
|
if (FsCtl->pInputBuffer != NULL) {
|
||||||
status = STATUS_INVALID_PARAMETER;
|
status = STATUS_INVALID_PARAMETER;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
} */
|
||||||
if (!FsCtl->pOutputBuffer) {
|
if (!FsCtl->pOutputBuffer) {
|
||||||
status = STATUS_INVALID_USER_BUFFER;
|
status = STATUS_INVALID_USER_BUFFER;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue