blob: 43eff3b4a3a98d4e7adb590cf3a1e38e22b0f041 [file] [log] [blame] [raw]
/* A part of the Native C Library for Windows NT
Copyright 2007-2015 PC GO Ld.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*/
#include <windows.h>
#include <nt.h>
#include <errno.h>
#include <pathname.h>
#include <ntstatus.h>
int rename(const char *oldfile, const char *newfile) {
if(!oldfile || !newfile) {
errno = EFAULT;
return -1;
}
if(!*oldfile || !*newfile) {
errno = ENOENT;
return -1;
}
void *handle;
IO_STATUS_BLOCK io_status;
unsigned long int file_share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
unsigned long int options = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT;
UNICODE_STRING old_path;
RtlCreateUnicodeStringFromAsciiz(&old_path, oldfile);
PATHNAME_UNIX2NT_UTF16_STRUCT(old_path);
OBJECT_ATTRIBUTES old_object = { sizeof(OBJECT_ATTRIBUTES), NULL, &old_path, 0, NULL, NULL };
long int status = NtOpenFile(&handle, FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE, &old_object, &io_status, file_share, options);
options &= ~FILE_OPEN_REPARSE_POINT;
if(status < 0) {
if(status == STATUS_INVALID_PARAMETER) {
status = NtOpenFile(&handle, DELETE | SYNCHRONIZE, &old_object, &io_status, file_share, options);
RtlFreeUnicodeString(&old_path);
if(status < 0) goto failed;
} else {
RtlFreeUnicodeString(&old_path);
failed:
__set_errno_from_ntstatus(status);
return -1;
}
} else {
FILE_ATTRIBUTE_TAG_INFORMATION tag;
status = NtQueryInformationFile(handle, &io_status, &tag, sizeof tag, FileAttributeTagInformation);
if(status < 0) {
RtlFreeUnicodeString(&old_path);
if(status != STATUS_NOT_IMPLEMENTED && status != STATUS_INVALID_PARAMETER) goto failed;
} else if((tag.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && tag.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
NtClose(handle);
status = NtOpenFile(&handle, DELETE | SYNCHRONIZE, &old_object, &io_status, file_share, options);
RtlFreeUnicodeString(&old_path);
if(__set_errno_from_ntstatus(status)) return -1;
}
}
UNICODE_STRING new_path;
RtlCreateUnicodeStringFromAsciiz(&new_path, newfile);
PATHNAME_UNIX2NT_UTF16_STRUCT(new_path);
OBJECT_ATTRIBUTES new_object = { sizeof(OBJECT_ATTRIBUTES), NULL, &new_path, 0, NULL, NULL };
void *new_fh;
status = NtOpenFile(&new_fh, FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE, &new_object, &io_status, file_share, FILE_OPEN_REPARSE_POINT | options);
if(status >= 0) {
FILE_INTERNAL_INFORMATION fii1, fii2;
if(NtQueryInformationFile(handle, &io_status, &fii1, sizeof fii1, FileInternalInformation) >= 0 &&
NtQueryInformationFile(new_fh, &io_status, &fii2, sizeof fii2, FileInternalInformation) >= 0 &&
fii1.IndexNumber.QuadPart == fii2.IndexNumber.QuadPart) {
RtlFreeUnicodeString(&new_path);
NtClose(handle);
NtClose(new_fh);
return 0;
}
NtClose(new_fh);
}
FILE_RENAME_INFORMATION *new_file_name = malloc(sizeof(FILE_RENAME_INFORMATION) + new_path.MaximumLength);
if(!new_file_name) {
NtClose(handle);
RtlFreeUnicodeString(&new_path);
errno = ENOMEM;
return -1;
}
memcpy(new_file_name->FileName, new_path.Buffer, new_path.MaximumLength);
new_file_name->ReplaceIfExists = 1;
new_file_name->RootDirectory = NULL;
new_file_name->FileNameLength = new_path.Length;
status = NtSetInformationFile(handle, &io_status, new_file_name, sizeof(FILE_RENAME_INFORMATION) + new_path.MaximumLength, FileRenameInformation);
free(new_file_name);
RtlFreeUnicodeString(&new_path);
NtClose(handle);
return __set_errno_from_ntstatus(status) ? -1 : 0;
}