* path.h (path_conv::fs_type): New method.
* syscalls.cc (rename): Check for cross-device situation before touching anything. Explain why. Workaround NFS bug in call to NtSetInformationFile(FileRenameInformation).
This commit is contained in:
parent
211f1ec717
commit
34ce80888a
@ -1,3 +1,10 @@
|
|||||||
|
2013-01-10 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
* path.h (path_conv::fs_type): New method.
|
||||||
|
* syscalls.cc (rename): Check for cross-device situation before
|
||||||
|
touching anything. Explain why. Workaround NFS bug in call to
|
||||||
|
NtSetInformationFile(FileRenameInformation).
|
||||||
|
|
||||||
2013-01-09 Corinna Vinschen <corinna@vinschen.de>
|
2013-01-09 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
* cygerrno.h: Fix copyright.
|
* cygerrno.h: Fix copyright.
|
||||||
|
@ -362,6 +362,7 @@ class path_conv
|
|||||||
bool fs_is_cifs () const {return fs.is_cifs ();}
|
bool fs_is_cifs () const {return fs.is_cifs ();}
|
||||||
bool fs_is_nwfs () const {return fs.is_nwfs ();}
|
bool fs_is_nwfs () const {return fs.is_nwfs ();}
|
||||||
bool fs_is_ncfsd () const {return fs.is_ncfsd ();}
|
bool fs_is_ncfsd () const {return fs.is_ncfsd ();}
|
||||||
|
fs_info_type fs_type () const {return fs.what_fs ();}
|
||||||
ULONG fs_serial_number () const {return fs.serial_number ();}
|
ULONG fs_serial_number () const {return fs.serial_number ();}
|
||||||
inline const char *set_path (const char *p)
|
inline const char *set_path (const char *p)
|
||||||
{
|
{
|
||||||
|
@ -2297,6 +2297,15 @@ rename (const char *oldpath, const char *newpath)
|
|||||||
}
|
}
|
||||||
dstpc = (removepc == &newpc) ? &new2pc : &newpc;
|
dstpc = (removepc == &newpc) ? &new2pc : &newpc;
|
||||||
|
|
||||||
|
/* Check cross-device before touching anything. Otherwise we might end
|
||||||
|
up with an unlinked target dir even if the actual rename didn't work. */
|
||||||
|
if (oldpc.fs_type () != dstpc->fs_type ()
|
||||||
|
|| oldpc.fs_serial_number () != dstpc->fs_serial_number ())
|
||||||
|
{
|
||||||
|
set_errno (EXDEV);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Opening the file must be part of the transaction. It's not sufficient
|
/* Opening the file must be part of the transaction. It's not sufficient
|
||||||
to call only NtSetInformationFile under the transaction. Therefore we
|
to call only NtSetInformationFile under the transaction. Therefore we
|
||||||
have to start the transaction here, if necessary. */
|
have to start the transaction here, if necessary. */
|
||||||
@ -2431,17 +2440,66 @@ retry:
|
|||||||
}
|
}
|
||||||
NtClose (nfh);
|
NtClose (nfh);
|
||||||
}
|
}
|
||||||
size = sizeof (FILE_RENAME_INFORMATION)
|
if (oldpc.fs_is_nfs ())
|
||||||
+ dstpc->get_nt_native_path ()->Length;
|
{
|
||||||
if (size > NT_MAX_PATH * sizeof (WCHAR)) /* Hopefully very seldom. */
|
/* Workaround depressing NFS bug. FILE_RENAME_INFORMATION.FileName
|
||||||
pfri = (PFILE_RENAME_INFORMATION) alloca (size);
|
*must* be relative to the parent directory of the original file,
|
||||||
|
otherwise NtSetInformationFile returns with STATUS_NOT_SAME_DEVICE.
|
||||||
|
Neither absolute paths, nor directory handle relative paths work
|
||||||
|
as expected! */
|
||||||
|
PWCHAR oldp, dstp;
|
||||||
|
|
||||||
|
/* Skip equivalent path prefix. We already know that both paths are
|
||||||
|
on the same drive anyway. */
|
||||||
|
for (oldp = oldpc.get_nt_native_path ()->Buffer,
|
||||||
|
dstp = dstpc->get_nt_native_path ()->Buffer;
|
||||||
|
*oldp == *dstp; ++oldp, ++dstp)
|
||||||
|
;
|
||||||
|
while (oldp[-1] != L'\\')
|
||||||
|
--oldp, --dstp;
|
||||||
|
/* Now oldp points to the first path component in oldpc different from
|
||||||
|
dstpc, vice versa for dstp and oldpc. To create a dstpc path relative
|
||||||
|
to oldpc, we now have to prepend as many ".." components to dstp, as
|
||||||
|
are still available in oldp. Example:
|
||||||
|
|
||||||
|
oldpc = \??\UNC\server\a\b\c\d\e
|
||||||
|
dstpc = \??\UNC\server\a\b\f\g
|
||||||
|
|
||||||
|
prefix: \??\UNC\server\a\b\
|
||||||
|
oldp: c\d\e
|
||||||
|
dstp: f\g
|
||||||
|
dstp expressed relative to e's parent dir: ..\..\f\g
|
||||||
|
|
||||||
|
So what we do here is to count the number of backslashes in oldp and
|
||||||
|
prepend one "..\" to dstp for each of them. */
|
||||||
|
PWCHAR newdst = tp.w_get ();
|
||||||
|
PWCHAR newp = newdst;
|
||||||
|
while ((oldp = wcschr (++oldp, L'\\')) != NULL)
|
||||||
|
newp = wcpcpy (newp, L"..\\");
|
||||||
|
newp = wcpcpy (newp, dstp);
|
||||||
|
size = sizeof (FILE_RENAME_INFORMATION)
|
||||||
|
+ (newp - newdst) * sizeof (WCHAR);
|
||||||
|
if (size > NT_MAX_PATH * sizeof (WCHAR)) /* Hopefully very seldom. */
|
||||||
|
pfri = (PFILE_RENAME_INFORMATION) alloca (size);
|
||||||
|
else
|
||||||
|
pfri = (PFILE_RENAME_INFORMATION) tp.w_get ();
|
||||||
|
pfri->FileNameLength = (newp - newdst) * sizeof (WCHAR);
|
||||||
|
memcpy (&pfri->FileName, newdst, pfri->FileNameLength);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
pfri = (PFILE_RENAME_INFORMATION) tp.w_get ();
|
{
|
||||||
|
size = sizeof (FILE_RENAME_INFORMATION)
|
||||||
|
+ dstpc->get_nt_native_path ()->Length;
|
||||||
|
if (size > NT_MAX_PATH * sizeof (WCHAR)) /* Hopefully very seldom. */
|
||||||
|
pfri = (PFILE_RENAME_INFORMATION) alloca (size);
|
||||||
|
else
|
||||||
|
pfri = (PFILE_RENAME_INFORMATION) tp.w_get ();
|
||||||
|
pfri->FileNameLength = dstpc->get_nt_native_path ()->Length;
|
||||||
|
memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer,
|
||||||
|
pfri->FileNameLength);
|
||||||
|
}
|
||||||
pfri->ReplaceIfExists = TRUE;
|
pfri->ReplaceIfExists = TRUE;
|
||||||
pfri->RootDirectory = NULL;
|
pfri->RootDirectory = NULL;
|
||||||
pfri->FileNameLength = dstpc->get_nt_native_path ()->Length;
|
|
||||||
memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer,
|
|
||||||
pfri->FileNameLength);
|
|
||||||
status = NtSetInformationFile (fh, &io, pfri, size, FileRenameInformation);
|
status = NtSetInformationFile (fh, &io, pfri, size, FileRenameInformation);
|
||||||
/* This happens if the access rights don't allow deleting the destination.
|
/* This happens if the access rights don't allow deleting the destination.
|
||||||
Even if the handle to the original file is opened with BACKUP
|
Even if the handle to the original file is opened with BACKUP
|
||||||
|
Loading…
x
Reference in New Issue
Block a user