Commit 8d5a2a99 authored by Erich E. Hoover's avatar Erich E. Hoover Committed by Vitaly Lipatov

server: Add support for deleting junction points with RemoveDirectory.

parent 13be6a07
......@@ -5371,7 +5371,7 @@ static void test_reparse_points(void)
REPARSE_GUID_DATA_BUFFER guid_buffer;
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags;
DWORD dwret, dwLen, dwFlags, err;
IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
HANDLE handle;
......@@ -5465,6 +5465,38 @@ static void test_reparse_points(void)
"Junction point folder's access time does not match.\n");
CloseHandle(handle);
/* Check deleting a junction point as if it were a directory */
HeapFree(GetProcessHeap(), 0, buffer);
handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
buffer_len = build_reparse_buffer(nameW.Buffer, &buffer);
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
CloseHandle(handle);
bret = RemoveDirectoryW(reparse_path);
ok(bret, "Failed to delete junction point as directory!\n");
dwret = GetFileAttributesW(reparse_path);
ok(dwret == (DWORD)~0, "Junction point still exists (attributes: 0x%x)!\n", dwret);
/* Check deleting a junction point as if it were a file */
HeapFree(GetProcessHeap(), 0, buffer);
bret = CreateDirectoryW(reparse_path, NULL);
ok(bret, "Failed to create junction point target directory.\n");
handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
buffer_len = build_reparse_buffer(nameW.Buffer, &buffer);
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
CloseHandle(handle);
bret = DeleteFileW(reparse_path);
ok(!bret, "Succeeded in deleting junction point as file!\n");
err = GetLastError();
ok(err == ERROR_ACCESS_DENIED, "Expected last error 0x%x for DeleteFile on junction point (actually 0x%x)!\n",
ERROR_ACCESS_DENIED, err);
dwret = GetFileAttributesW(reparse_path);
ok(dwret != (DWORD)~0, "Junction point doesn't exist (attributes: 0x%x)!\n", dwret);
ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a junction point! (attributes: 0x%x)\n", dwret);
cleanup:
/* Cleanup */
pRtlFreeUnicodeString(&nameW);
......
......@@ -163,7 +163,8 @@ struct closed_fd
struct list entry; /* entry in inode closed list */
int unix_fd; /* the unix file descriptor */
int unlink; /* whether to unlink on close: -1 - implicit FILE_DELETE_ON_CLOSE, 1 - explicit disposition */
char *unix_name; /* name to unlink on close, points to parent fd unix_name */
char *unlink_name; /* name to unlink on close, points to parent fd unix_name */
char *unix_name; /* name to real file path, points to parent fd unix_name */
};
struct fd
......@@ -178,6 +179,7 @@ struct fd
unsigned int access; /* file access (FILE_READ_DATA etc.) */
unsigned int options; /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */
unsigned int sharing; /* file sharing mode */
char *unlink_name; /* file name to unlink on close */
char *unix_name; /* unix file name */
WCHAR *nt_name; /* NT file name */
data_size_t nt_namelen; /* length of NT file name */
......@@ -1125,6 +1127,7 @@ static void inode_close_pending( struct inode *inode, int keep_unlinks )
if (!keep_unlinks || !fd->unlink) /* get rid of it unless there's an unlink pending on that file */
{
list_remove( ptr );
free( fd->unlink_name );
free( fd->unix_name );
free( fd );
}
......@@ -1159,12 +1162,13 @@ static void inode_destroy( struct object *obj )
{
/* make sure it is still the same file */
struct stat st;
if (!stat( fd->unix_name, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino)
if (!lstat( fd->unlink_name, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino)
{
if (S_ISDIR(st.st_mode)) rmdir( fd->unix_name );
else unlink( fd->unix_name );
if (S_ISDIR(st.st_mode)) rmdir( fd->unlink_name );
else unlink( fd->unlink_name );
}
}
free( fd->unlink_name );
free( fd->unix_name );
free( fd );
}
......@@ -1585,6 +1589,7 @@ static void fd_destroy( struct object *obj )
else /* no inode, close it right away */
{
if (fd->unix_fd != -1) close( fd->unix_fd );
free( fd->unlink_name );
free( fd->unix_name );
}
}
......@@ -1694,6 +1699,7 @@ static struct fd *alloc_fd_object(void)
fd->options = 0;
fd->sharing = 0;
fd->unix_fd = -1;
fd->unlink_name = NULL;
fd->unix_name = NULL;
fd->nt_name = NULL;
fd->nt_namelen = 0;
......@@ -1731,6 +1737,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
fd->access = 0;
fd->options = options;
fd->sharing = 0;
fd->unlink_name = NULL;
fd->unix_name = NULL;
fd->nt_name = NULL;
fd->nt_namelen = 0;
......@@ -1772,6 +1779,12 @@ struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sha
fd->nt_namelen = orig->nt_namelen;
}
if (orig->unlink_name)
{
if (!(fd->unlink_name = mem_alloc( strlen(orig->unlink_name) + 1 ))) goto failed;
strcpy( fd->unlink_name, orig->unlink_name );
}
if (orig->inode)
{
struct closed_fd *closed = mem_alloc( sizeof(*closed) );
......@@ -1784,6 +1797,7 @@ struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sha
}
closed->unix_fd = fd->unix_fd;
closed->unlink = 0;
closed->unlink_name = fd->unlink_name;
closed->unix_name = fd->unix_name;
fd->closed = closed;
fd->inode = (struct inode *)grab_object( orig->inode );
......@@ -1972,18 +1986,19 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
fd->unix_name = NULL;
if ((path = dup_fd_name( root, name )))
{
fd->unlink_name = path;
fd->unix_name = realpath( path, NULL );
free( path );
}
closed_fd->unix_fd = fd->unix_fd;
closed_fd->unlink = 0;
closed_fd->unlink_name = fd->unlink_name;
closed_fd->unix_name = fd->unix_name;
fstat( fd->unix_fd, &st );
lstat( fd->unlink_name, &st );
*mode = st.st_mode;
/* only bother with an inode for normal files and directories */
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
{
unsigned int err;
struct inode *inode = get_inode( st.st_dev, st.st_ino, fd->unix_fd );
......@@ -2001,6 +2016,9 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
list_add_head( &inode->open, &fd->inode_entry );
closed_fd = NULL;
fstat( fd->unix_fd, &st );
*mode = st.st_mode;
/* check directory options */
if ((options & FILE_DIRECTORY_FILE) && !S_ISDIR(st.st_mode))
{
......@@ -2648,10 +2666,11 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da
free( fd->nt_name );
fd->nt_name = dup_nt_name( root, nt_name, &fd->nt_namelen );
free( fd->unlink_name );
free( fd->unix_name );
fd->closed->unlink_name = fd->unlink_name = name;
fd->closed->unix_name = fd->unix_name = realpath( name, NULL );
free( name );
if (!fd->unix_name)
if (!fd->unlink_name || !fd->unix_name)
set_error( STATUS_NO_MEMORY );
return;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment