NTSTATUS status;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
- status = NtOpenFile (&fh, 0, pc.get_object_attr (attr, sec_none_nih), &io,
+ status = NtOpenFile (&fh, FILE_ANY_ACCESS,
+ pc.get_object_attr (attr, sec_none_nih), &io,
FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
if (!NT_SUCCESS (status))
IO_STATUS_BLOCK io;
FILE_BASIC_INFORMATION fbi;
fbi.CreationTime.QuadPart = 0LL;
- fbi.LastAccessTime= lastaccess;
+ fbi.LastAccessTime = lastaccess;
fbi.LastWriteTime = lastwrite;
fbi.ChangeTime.QuadPart = 0LL;
fbi.FileAttributes = 0;
/* FIXME: The correct way to do this to get POSIX locking semantics is to
keep a linked list of posix lock requests and map them into Win32 locks.
he problem is that Win32 does not deal correctly with overlapping lock
- requests. Also another pain is that Win95 doesn't do non-blocking or
- non-exclusive locks at all. For '95 just convert all lock requests into
- blocking,exclusive locks. This shouldn't break many apps but denying all
- locking would. For now just convert to Win32 locks and hope for
- the best. */
+ requests. */
int
fhandler_disk_file::lock (int cmd, struct __flock64 *fl)
set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
&sa, sd);
- if (CreateDirectoryA (get_win32_name (), &sa))
- {
+ NTSTATUS status;
+ HANDLE dir;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK io;
+ ULONG fattr = FILE_ATTRIBUTE_DIRECTORY;
#ifdef HIDDEN_DOT_FILES
- char *c = strrchr (real_dir.get_win32 (), '\\');
- if ((c && c[1] == '.') || *get_win32_name () == '.')
- SetFileAttributes (get_win32_name (), FILE_ATTRIBUTE_HIDDEN);
+ UNICODE_STRING basename;
+
+ RtlSplitUnicodePath (pc.get_nt_native_path (), NULL, &basename);
+ if (basename.Buffer[0] == L'.')
+ fattr |= FILE_ATTRIBUTE_HIDDEN;
#endif
+ status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ pc.get_object_attr (attr, sa), &io, NULL,
+ fattr, FILE_SHARE_VALID_FLAGS, FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
+ | FILE_OPEN_FOR_BACKUP_INTENT,
+ NULL, 0);
+ if (NT_SUCCESS (status))
+ {
+ NtClose (dir);
res = 0;
}
else
- __seterrno ();
+ __seterrno_from_nt_status (status);
return res;
}
int
fhandler_disk_file::rmdir ()
{
- extern DWORD unlink_nt (path_conv &win32_name, bool setattrs);
-
- int res = -1;
+ extern NTSTATUS unlink_nt (path_conv &pc);
if (!pc.isdir ())
{
set_errno (ENOTDIR);
return -1;
}
- /* Even own directories can't be removed if R/O attribute is set. */
- if (pc.has_attribute (FILE_ATTRIBUTE_READONLY))
- SetFileAttributes (get_win32_name (),
- (DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
-
- DWORD err, att = 0;
-
- int rc = !(err = unlink_nt (pc, pc.has_attribute (FILE_ATTRIBUTE_READONLY)));
- if (err)
- SetLastError (err);
+ if (!pc.exists ())
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
- if (isremote () && exists ())
- att = GetFileAttributes (get_win32_name ());
+ NTSTATUS status = unlink_nt (pc);
- /* Sometimes smb indicates failure when it really succeeds, so check for
- this case specifically. */
- if (rc || att == INVALID_FILE_ATTRIBUTES)
+ /* Check for existence of remote dirs after trying to delete them.
+ Two reasons:
+ - Sometimes SMB indicates failure when it really succeeds.
+ - Removeing a directory on a samba drive doesn't return an error if the
+ directory can't be removed because it's not empty. */
+ if (isremote ())
{
- /* RemoveDirectory on a samba drive doesn't return an error if the
- directory can't be removed because it's not empty. Checking for
- existence afterwards keeps us informed about success. */
- if (!isremote () || att == INVALID_FILE_ATTRIBUTES)
- return 0;
+ OBJECT_ATTRIBUTES attr;
+ FILE_BASIC_INFORMATION fbi;
- err = ERROR_DIR_NOT_EMPTY;
+ if (NT_SUCCESS (NtQueryAttributesFile
+ (pc.get_object_attr (attr, sec_none_nih), &fbi)))
+ status = STATUS_DIRECTORY_NOT_EMPTY;
+ else
+ status = STATUS_SUCCESS;
}
- else
- err = GetLastError ();
-
- __seterrno_from_win_error (err);
-
- return res;
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ return 0;
}
/* This is the minimal number of entries which fit into the readdir cache.
recycler, status);
}
-DWORD
-unlink_nt (path_conv &win32_name, bool setattrs)
+NTSTATUS
+unlink_nt (path_conv &pc)
{
+ NTSTATUS status;
+ HANDLE fh;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
- NTSTATUS status;
- HANDLE h;
+ FILE_BASIC_INFORMATION fbi;
+
+ ACCESS_MASK access = DELETE;
+ /* If one of the R/O attributes is set, we have to open the file with
+ FILE_WRITE_ATTRIBUTES to be able to remove these flags before trying
+ to delete it. */
+ if (pc.file_attributes () & (FILE_ATTRIBUTE_READONLY
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_HIDDEN))
+ access |= FILE_WRITE_ATTRIBUTES;
ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT;
- /* Don't open directories with "delete on close", because the NT internal
- semantic is apparently different from the file semantic. If a directory
- is opened "delete on close", the rename operation in try_to_bin fails
- with STATUS_ACCESS_DENIED. So directories must be deleted using
- NtSetInformationFile, class FileDispositionInformation, which works fine.
-
- Correction, moving a directory opened with delete-on-close fails ONLY
- on XP. Note to myself: Never take anything for granted on Windows!
-
- Don't try "delete on close" if the file is on a remote share. If two
- processes have open handles on a file and one of them calls unlink, then
- it happens that the file is removed from the remote share even though the
- other process still has an open handle. This other process than gets
- Win32 error 59, ERROR_UNEXP_NET_ERR when trying to access the file. That
- does not happen when using NtSetInformationFile, class
- FileDispositionInformation, which nicely succeeds but still, the file is
- available for the other process. Microsoft KB 837665 describes this
- problem as a bug in 2K3, but I have reproduced it on shares on Samba
- 2.2.8, Samba 3.0.2, NT4SP6, XP64SP1 and 2K3 and in all cases, DeleteFile
- works, "delete on close" does not. */
- if (!win32_name.isdir () && !win32_name.isremote ())
- flags |= FILE_DELETE_ON_CLOSE;
/* Add the reparse point flag to native symlinks, otherwise we remove the
target, not the symlink. */
- if (win32_name.is_rep_symlink ())
+ if (pc.is_rep_symlink ())
flags |= FILE_OPEN_REPARSE_POINT;
- win32_name.get_object_attr (attr, sec_none_nih);
+ pc.get_object_attr (attr, sec_none_nih);
/* First try to open the file with sharing not allowed. If the file
has an open handle on it, this will fail. That indicates that the
file has to be moved to the recycle bin so that it actually disappears
doesn't fail, the file is not in use and by simply closing the handle
the file will disappear. */
bool move_to_bin = false;
- status = NtOpenFile (&h, DELETE, &attr, &io, 0, flags);
+ status = NtOpenFile (&fh, access, &attr, &io, 0, flags);
if (status == STATUS_SHARING_VIOLATION)
{
move_to_bin = true;
- if (!win32_name.isdir () || win32_name.isremote ())
- status = NtOpenFile (&h, DELETE, &attr, &io, FILE_SHARE_VALID_FLAGS,
- flags);
+ if (!pc.isdir () || pc.isremote ())
+ status = NtOpenFile (&fh, access, &attr, &io,
+ FILE_SHARE_VALID_FLAGS, flags);
else
{
/* It's getting tricky. The directory is opened in some process,
and another one. Three entries means, not empty. This doesn't
work for the root directory of a drive, but the root dir can
neither be deleted, nor moved anyway. */
- status = NtOpenFile (&h, DELETE | SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ status = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY | SYNCHRONIZE,
&attr, &io, FILE_SHARE_VALID_FLAGS,
flags | FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS (status))
+ 3 * NAME_MAX * sizeof (WCHAR);
PFILE_NAMES_INFORMATION pfni = (PFILE_NAMES_INFORMATION)
alloca (bufsiz);
- status = NtQueryDirectoryFile (h, NULL, NULL, 0, &io, pfni,
+ status = NtQueryDirectoryFile (fh, NULL, NULL, 0, &io, pfni,
bufsiz, FileNamesInformation,
FALSE, NULL, TRUE);
if (!NT_SUCCESS (status))
{
- NtClose (h);
+ NtClose (fh);
syscall_printf ("Checking if directory is empty failed, "
"status = %p", status);
- return RtlNtStatusToDosError (status);
+ return status;
}
int cnt = 1;
while (pfni->NextEntryOffset)
}
if (cnt > 2)
{
- NtClose (h);
+ NtClose (fh);
syscall_printf ("Directory not empty");
- return ERROR_DIR_NOT_EMPTY;
+ return STATUS_DIRECTORY_NOT_EMPTY;
}
}
}
{
if (status == STATUS_DELETE_PENDING)
{
- syscall_printf ("Delete already pending, status = %p", status);
+ syscall_printf ("Delete already pending");
return 0;
}
syscall_printf ("Opening file for delete failed, status = %p", status);
- return RtlNtStatusToDosError (status);
+ return status;
}
- if (setattrs)
- SetFileAttributes (win32_name, (DWORD) win32_name);
-
- if (move_to_bin && !win32_name.isremote ())
- try_to_bin (win32_name, h);
+ if (move_to_bin && !pc.isremote ())
+ try_to_bin (pc, fh);
- DWORD lasterr = 0;
-
- if (win32_name.isdir () || win32_name.isremote ())
+ /* Get rid of read-only attributes. */
+ if (access & FILE_WRITE_ATTRIBUTES)
{
- FILE_DISPOSITION_INFORMATION disp = { TRUE };
- status = NtSetInformationFile (h, &io, &disp, sizeof disp,
- FileDispositionInformation);
- if (!NT_SUCCESS (status))
- {
- syscall_printf ("Setting delete disposition failed, status = %p",
- status);
- lasterr = RtlNtStatusToDosError (status);
- }
+ FILE_BASIC_INFORMATION fbi;
+ fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart =
+ fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
+ fbi.FileAttributes = pc.file_attributes ()
+ & ~(FILE_ATTRIBUTE_READONLY
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_HIDDEN);
+ NtSetInformationFile (fh, &io, &fbi, sizeof fbi, FileBasicInformation);
}
- status = NtClose (h);
+ FILE_DISPOSITION_INFORMATION disp = { TRUE };
+ status = NtSetInformationFile (fh, &io, &disp, sizeof disp,
+ FileDispositionInformation);
if (!NT_SUCCESS (status))
{
- /* Maybe that's really paranoid, but not being able to close the file
- also means that deleting fails. */
- syscall_printf ("%p = NtClose (%p)", status, h);
- if (!lasterr)
- lasterr = RtlNtStatusToDosError (status);
+ syscall_printf ("Setting delete disposition failed, status = %p", status);
+ /* Restore R/O attributes. */
+ if (access & FILE_WRITE_ATTRIBUTES)
+ {
+ fbi.FileAttributes = pc.file_attributes ();
+ NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
+ FileBasicInformation);
+ }
}
- syscall_printf ("Deleting succeeded");
- return lasterr;
+ NtClose (fh);
+ return status;
}
extern "C" int
{
int res = -1;
DWORD devn;
- DWORD lasterr;
+ NTSTATUS status;
path_conv win32_name (ourname, PC_SYM_NOFOLLOW,
transparent_exe ? stat_suffixes : NULL);
goto done;
}
- bool setattrs;
- if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY
- | FILE_ATTRIBUTE_SYSTEM
- | FILE_ATTRIBUTE_HIDDEN)))
- setattrs = false;
- else
- {
- /* Allow us to delete even if read-only */
- setattrs = SetFileAttributes (win32_name,
- (DWORD) win32_name
- & ~(FILE_ATTRIBUTE_READONLY
- | FILE_ATTRIBUTE_SYSTEM
- | FILE_ATTRIBUTE_HIDDEN));
- }
-
- lasterr = unlink_nt (win32_name, setattrs);
- if (!lasterr)
+ status = unlink_nt (win32_name);
+ if (NT_SUCCESS (status))
res = 0;
else
{
- SetFileAttributes (win32_name, (DWORD) win32_name);
-
/* FIXME: Can we get rid of the delqueue now? */
- if (lasterr == ERROR_SHARING_VIOLATION)
+ if (status == STATUS_SHARING_VIOLATION)
{
/* Add file to the "to be deleted" queue. */
syscall_printf ("Sharing violation, couldn't delete file");
res = 0;
}
else
- __seterrno_from_win_error (lasterr);
+ __seterrno_from_nt_status (status);
}
done:
else if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (),
MOVEFILE_REPLACE_EXISTING))
res = 0;
- else if ((lasterr = unlink_nt (real_new, false)))
+ else if ((lasterr = unlink_nt (real_new)))
{
SetLastError (lasterr);
syscall_printf ("Can't remove target file/dir, %E");
if (lnk_suffix)
{
*lnk_suffix = '.';
- unlink_nt (real_new, false);
+ unlink_nt (real_new);
}
/* Shortcut hack, No. 3, part 2 */
/* If a file with the given name exists, it must be deleted after the
{
lnk_suffix = strrchr (real_new.get_win32 (), '.');
*lnk_suffix = '\0';
- unlink_nt (real_new, false);
+ unlink_nt (real_new);
}
}