symlink: handle_open() detects symlink creation

added check in handle_open() to avoid calling CREATE/OPEN when we're creating a symlink:

if (args->disposition == FILE_CREATE &&
    args->access_mask == (FILE_WRITE_ATTRIBUTES | SYNCHRONIZE | DELETE) &&
    args->access_mode == 0 &&
    args->create_opts & FILE_OPEN_REPARSE_POINT)

these are the open arguments we get from the CreateSymbolicLink() syscall.  by avoiding the call to CREATE/OPEN on handle_open(), we save ourselves from having to REMOVE the file before creating the symlink

added a check to handle_symlink() in case the file was actually created on open (an application could open the file with different arguments, and send the FSCTL_SET_REPARSE_POINT manually), and removes the file first

Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
Casey Bodley 2010-09-23 12:59:26 -04:00 committed by unknown
parent 62fa6176be
commit ccdaa169eb
4 changed files with 42 additions and 6 deletions

View file

@ -132,10 +132,10 @@ static BOOLEAN do_lookup(uint32_t type, ULONG access_mask, ULONG disposition)
} }
if ((access_mask & FILE_READ_DATA) || if ((access_mask & FILE_READ_DATA) ||
(access_mask & FILE_WRITE_DATA) || (access_mask & FILE_WRITE_DATA) ||
(access_mask & FILE_APPEND_DATA) || (access_mask & FILE_APPEND_DATA) ||
(access_mask & FILE_EXECUTE)) (access_mask & FILE_EXECUTE))
return FALSE; return FALSE;
else { else {
dprintf(1, "Open call that wants to manage attributes\n"); dprintf(1, "Open call that wants to manage attributes\n");
return TRUE; return TRUE;
@ -274,7 +274,35 @@ int handle_open(nfs41_upcall *upcall)
state->type = info.type; state->type = info.type;
} else if (status != ERROR_FILE_NOT_FOUND) } else if (status != ERROR_FILE_NOT_FOUND)
goto out_free_state; goto out_free_state;
if (do_lookup(state->type, args->access_mask, args->disposition)) {
/* 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
* is troublesome for two reasons:
* -an application might use these exact arguments to create a normal
* file, and we would return success without actually creating it
* -an application could create a symlink by sending the FSCTL to set
* the reparse point manually, and their open might be different. in
* this case we'd create the file on open, and need to remove it
* before creating the symlink */
if (args->disposition == FILE_CREATE &&
args->access_mask == (FILE_WRITE_ATTRIBUTES | SYNCHRONIZE | DELETE) &&
args->access_mode == 0 &&
args->create_opts & FILE_OPEN_REPARSE_POINT) {
/* fail if the file already exists */
uint32_t create;
status = map_disposition_2_nfsopen(args->disposition, status,
&create, &upcall->last_error);
if (status)
goto out_free_state;
/* defer the call to CREATE until we get the symlink set upcall */
dprintf(1, "trying to create a symlink, deferring create\n");
/* because of WRITE_ATTR access, be prepared for a setattr upcall;
* will crash if the superblock is null, so use the parent's */
state->file.fh.superblock = state->parent.fh.superblock;
} else if (do_lookup(state->type, args->access_mask, args->disposition)) {
if (status) { if (status) {
dprintf(1, "nfs41_lookup failed with %d\n", status); dprintf(1, "nfs41_lookup failed with %d\n", status);
goto out_free_state; goto out_free_state;

View file

@ -188,6 +188,12 @@ int handle_symlink(nfs41_upcall *upcall)
int status = NO_ERROR; int status = NO_ERROR;
if (args->set) { if (args->set) {
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);
}
/* create the symlink */ /* create the symlink */
status = nfs41_create(state->session, NF4LNK, 0777, status = nfs41_create(state->session, NF4LNK, 0777,
args->target_set, &state->parent, &state->file); args->target_set, &state->parent, &state->file);

View file

@ -311,6 +311,7 @@ int nfs_to_windows_error(int status, int default_error)
case NFS4ERR_SYMLINK: case NFS4ERR_SYMLINK:
case NFS4ERR_WRONG_TYPE: return ERROR_INVALID_PARAMETER; case NFS4ERR_WRONG_TYPE: return ERROR_INVALID_PARAMETER;
case NFS4ERR_NOFILEHANDLE:
case NFS4ERR_OLD_STATEID: case NFS4ERR_OLD_STATEID:
case NFS4ERR_BAD_STATEID: case NFS4ERR_BAD_STATEID:
case NFS4ERR_ADMIN_REVOKED: return ERROR_FILE_INVALID; case NFS4ERR_ADMIN_REVOKED: return ERROR_FILE_INVALID;

View file

@ -3638,6 +3638,7 @@ static NTSTATUS map_setfile_error(DWORD error)
case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND; case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND;
case ERROR_PATH_NOT_FOUND: return STATUS_OBJECT_PATH_NOT_FOUND; case ERROR_PATH_NOT_FOUND: return STATUS_OBJECT_PATH_NOT_FOUND;
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED; case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
case ERROR_NOT_SAME_DEVICE: return STATUS_NOT_SAME_DEVICE; case ERROR_NOT_SAME_DEVICE: return STATUS_NOT_SAME_DEVICE;
case ERROR_NOT_SUPPORTED: return STATUS_NOT_IMPLEMENTED; case ERROR_NOT_SUPPORTED: return STATUS_NOT_IMPLEMENTED;
case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED; case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED;