blob: c90ca77ce930359ee53ceeb72fd980e1f4288a11 [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 <stdio.h>
#include <windows.h>
#include <nt.h>
#include <errno.h>
#include <pathname.h>
#include <ntstatus.h>
#include <sys/stat.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;
UNICODE_STRING old_path;
if(!RtlCreateUnicodeStringFromAsciiz(&old_path, oldfile)) {
errno = ENOMEM;
return -1;
}
PATHNAME_UNIX2NT_UTF16_STRUCT(old_path);
OBJECT_ATTRIBUTES old_object = { sizeof(OBJECT_ATTRIBUTES), NULL, &old_path, 0, NULL, NULL };
long int status = NtOpenSymbolicLinkObject(&handle, SYMBOLIC_LINK_ALL_ACCESS, &old_object);
//printf("nativelibc debug: rename: NtOpenSymbolicLinkObject status = 0x%lx\n", status);
if(status != STATUS_OBJECT_TYPE_MISMATCH) {
RtlFreeUnicodeString(&old_path);
if(status < 0) {
__set_errno_from_ntstatus(status);
return -1;
}
// Check other opens
OBJECT_BASIC_INFORMATION obi;
status = NtQueryObject(handle, ObjectBasicInformation, &obi, sizeof obi, NULL);
//printf("nativelibc debug: rename: NtQueryObject status = 0x%lx\n", status);
if(status < 0) {
NtClose(handle);
__set_errno_from_ntstatus(status);
return -1;
}
if(obi.HandleCount > 1) {
NtClose(handle);
errno = EBUSY;
return -1;
}
// Get target path
UNICODE_STRING rpath = { 0, 0, NULL };
unsigned long int path_size;
status = NtQuerySymbolicLinkObject(handle, &rpath, &path_size);
//printf("nativelibc debug: rename: NtQuerySymbolicLinkObject status = 0x%lx, path_size = %lu\n", status, path_size);
if(status > 0 && status != STATUS_BUFFER_TOO_SMALL) {
NtClose(handle);
__set_errno_from_ntstatus(status);
return -1;
}
rpath.Length = path_size - sizeof(wchar_t);
rpath.MaximumLength = path_size;
rpath.Buffer = malloc(path_size);
if(!rpath.Buffer) {
NtClose(handle);
errno = ENOMEM;
return -1;
}
status = NtQuerySymbolicLinkObject(handle, &rpath, NULL);
//printf("nativelibc debug: rename: NtQuerySymbolicLinkObject status = 0x%lx\n", status);
if(status < 0) {
NtClose(handle);
RtlFreeUnicodeString(&rpath);
__set_errno_from_ntstatus(status);
return -1;
}
// Create new symbolic link
void *new_handle;
UNICODE_STRING new_path;
if(!RtlCreateUnicodeStringFromAsciiz(&new_path, newfile)) {
RtlFreeUnicodeString(&rpath);
errno = ENOMEM;
return -1;
}
PATHNAME_UNIX2NT_UTF16_STRUCT(new_path);
OBJECT_ATTRIBUTES new_object = { sizeof(OBJECT_ATTRIBUTES), NULL, &new_path, OBJ_PERMANENT, NULL, NULL };
status = NtCreateSymbolicLinkObject(&new_handle, SYMBOLIC_LINK_ALL_ACCESS, &new_object, &rpath);
RtlFreeUnicodeString(&rpath);
RtlFreeUnicodeString(&new_path);
if(status < 0) {
NtClose(handle);
__set_errno_from_ntstatus(status);
return -1;
}
NtClose(new_handle);
// Remove old
status = NtMakeTemporaryObject(handle);
//printf("nativelibc debug: rename: NtMakeTemporaryObject status = 0x%lx\n", status);
NtClose(handle);
if(status < 0) {
__set_errno_from_ntstatus(status);
return -1;
}
return 0;
}
status = NtOpenDirectoryObject(&handle, DIRECTORY_ALL_ACCESS, &old_object);
if(status != STATUS_OBJECT_TYPE_MISMATCH) {
unsigned long int context = 0;
RtlFreeUnicodeString(&old_path);
if(status < 0) {
__set_errno_from_ntstatus(status);
return -1;
}
status = NtQueryDirectoryObject(handle, NULL, 0, 0, 1, &context, NULL);
if(status != STATUS_NO_MORE_ENTRIES) {
NtClose(handle);
if(status < 0) __set_errno_from_ntstatus(status);
else errno = EBUSY;
return -1;
}
OBJECT_BASIC_INFORMATION obi;
status = NtQueryObject(handle, ObjectBasicInformation, &obi, sizeof obi, NULL);
if(status < 0) {
NtClose(handle);
__set_errno_from_ntstatus(status);
return -1;
}
if(obi.HandleCount > 1) {
NtClose(handle);
errno = EBUSY;
return -1;
}
status = NtMakeTemporaryObject(handle);
NtClose(handle);
if(status < 0) {
__set_errno_from_ntstatus(status);
return -1;
}
return mkdir(newfile, 0777);
}
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;
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;
if(!RtlCreateUnicodeStringFromAsciiz(&new_path, newfile)) {
RtlFreeUnicodeString(&old_path);
errno = ENOMEM;
return -1;
}
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);
}
size_t fri_size = sizeof(FILE_RENAME_INFORMATION) + new_path.MaximumLength;
FILE_RENAME_INFORMATION *new_file_name = malloc(fri_size);
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, fri_size, FileRenameInformation);
free(new_file_name);
RtlFreeUnicodeString(&new_path);
NtClose(handle);
return __set_errno_from_ntstatus(status) ? -1 : 0;
}