int efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of file to delete. */ { DWORD attr; WCHAR *wname = (WCHAR *) name; if (DeleteFileW(wname) != FALSE) { return 1; } errno = errno_map(GetLastError()); if (errno == EACCES) { attr = GetFileAttributesW(wname); if (attr != (DWORD) -1) { if (attr & FILE_ATTRIBUTE_DIRECTORY) { /* * Windows NT reports removing a directory as EACCES instead * of EPERM. */ errno = EPERM; } } } else if (errno == ENOENT) { attr = GetFileAttributesW(wname); if (attr != (DWORD) -1) { if (attr & FILE_ATTRIBUTE_DIRECTORY) { /* * Windows 95 reports removing a directory as ENOENT instead * of EPERM. */ errno = EPERM; } } } else if (errno == EINVAL) { /* * Windows NT reports removing a char device as EINVAL instead of * EACCES. */ errno = EACCES; } return check_error(-1, errInfo); }
static int set_error(Efile_error* errInfo) { errInfo->posix_errno = errno_map(errInfo->os_errno = GetLastError()); return 0; }
int efile_rename(Efile_error* errInfo, /* Where to return error codes. */ char* src, /* Original name. */ char* dst) /* New name. */ { DWORD srcAttr, dstAttr; WCHAR *wsrc = (WCHAR *) src; WCHAR *wdst = (WCHAR *) dst; if (MoveFileW(wsrc, wdst) != FALSE) { return 1; } errno = errno_map(GetLastError()); srcAttr = GetFileAttributesW(wsrc); dstAttr = GetFileAttributesW(wdst); if (srcAttr == (DWORD) -1) { srcAttr = 0; } if (dstAttr == (DWORD) -1) { dstAttr = 0; } if (errno == EBADF) { errno = EACCES; return check_error(-1, errInfo); } if (errno == EACCES) { decode: if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) { WCHAR srcPath[MAX_PATH], dstPath[MAX_PATH]; WCHAR *srcRest, *dstRest; int size; size = GetFullPathNameW(wsrc, MAX_PATH, srcPath, &srcRest); if ((size == 0) || (size > MAX_PATH)) { return check_error(-1, errInfo); } size = GetFullPathNameW(wdst, MAX_PATH, dstPath, &dstRest); if ((size == 0) || (size > MAX_PATH)) { return check_error(-1, errInfo); } if (srcRest == NULL) { srcRest = srcPath + wcslen(srcPath); } if (_wcsnicmp(srcPath, dstPath, srcRest - srcPath) == 0) { /* * Trying to move a directory into itself. */ errno = EINVAL; } if (extract_root(srcPath)) { /* * Attempt to move a root directory. Never allowed. */ errno = EINVAL; } (void) extract_root(dstPath); if (dstPath[0] == L'\0') { /* * The filename was invalid. (Don't know why, * but play it safe.) */ errno = EINVAL; } if (_wcsicmp(srcPath, dstPath) != 0) { /* * If src is a directory and dst filesystem != src * filesystem, errno should be EXDEV. It is very * important to get this behavior, so that the caller * can respond to a cross filesystem rename by * simulating it with copy and delete. The MoveFile * system call already handles the case of moving a * *file* between filesystems. */ errno = EXDEV; } } /* * Other types of access failure is that dst is a read-only * filesystem, that an open file referred to src or dest, or that * src or dest specified the current working directory on the * current filesystem. EACCES is returned for those cases. */ } else if (errno == EEXIST) { /* * Reports EEXIST any time the target already exists. If it makes * sense, remove the old file and try renaming again. */ if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) { if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) { /* * Overwrite empty dst directory with src directory. The * following call will remove an empty directory. If it * fails, it's because it wasn't empty. */ if (RemoveDirectoryW(wdst)) { /* * Now that that empty directory is gone, we can try * renaming again. If that fails, we'll put this empty * directory back, for completeness. */ if (MoveFileW(wsrc, wdst) != FALSE) { return 1; } /* * Some new error has occurred. Don't know what it * could be, but report this one. */ errno = errno_map(GetLastError()); CreateDirectoryW(wdst, NULL); SetFileAttributesW(wdst, dstAttr); if (errno == EACCES) { /* * Decode the EACCES to a more meaningful error. */ goto decode; } } } else { /* (dstAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */ errno = ENOTDIR; } } else { /* (srcAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */ if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) { errno = EISDIR; } else { /* * Overwrite existing file by: * * 1. Rename existing file to temp name. * 2. Rename old file to new name. * 3. If success, delete temp file. If failure, * put temp file back to old name. */ WCHAR tempName[MAX_PATH]; int result, size; WCHAR *rest; size = GetFullPathNameW(wdst, MAX_PATH, tempName, &rest); if ((size == 0) || (size > MAX_PATH) || (rest == NULL)) { return check_error(-1, errInfo); } *rest = L'\0'; result = -1; if (GetTempFileNameW(tempName, L"erlr", 0, tempName) != 0) { /* * Strictly speaking, need the following DeleteFile and * MoveFile to be joined as an atomic operation so no * other app comes along in the meantime and creates the * same temp file. */ DeleteFileW(tempName); if (MoveFileW(wdst, tempName) != FALSE) { if (MoveFileW(wsrc, wdst) != FALSE) { SetFileAttributesW(tempName, FILE_ATTRIBUTE_NORMAL); DeleteFileW(tempName); return 1; } else { DeleteFileW(wdst); MoveFileW(tempName, wdst); } } /* * Can't backup dst file or move src file. Return that * error. Could happen if an open file refers to dst. */ errno = errno_map(GetLastError()); if (errno == EACCES) { /* * Decode the EACCES to a more meaningful error. */ goto decode; } } return result; } } } return check_error(-1, errInfo); }
int efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ char* name) /* Name of directory to delete. */ { OSVERSIONINFO os; DWORD attr; WCHAR *wname = (WCHAR *) name; if (RemoveDirectoryW(wname) != FALSE) { return 1; } errno = errno_map(GetLastError()); if (errno == EACCES) { attr = GetFileAttributesW(wname); if (attr != (DWORD) -1) { if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { /* * Windows 95 reports calling RemoveDirectory on a file as an * EACCES, not an ENOTDIR. */ errno = ENOTDIR; goto end; } /* * Windows 95 reports removing a non-empty directory as * an EACCES, not an EEXIST. If the directory is not empty, * change errno so caller knows what's going on. */ os.dwOSVersionInfoSize = sizeof(os); GetVersionEx(&os); if (os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { HANDLE handle; WIN32_FIND_DATAW data; WCHAR buffer[2*MAX_PATH]; int len; len = wcslen(wname); wcscpy(buffer, wname); if (buffer[0] && buffer[len-1] != L'\\' && buffer[len-1] != L'/') { wcscat(buffer, L"\\"); } wcscat(buffer, L"*.*"); handle = FindFirstFileW(buffer, &data); if (handle != INVALID_HANDLE_VALUE) { while (1) { if ((wcscmp(data.cFileName, L".") != 0) && (wcscmp(data.cFileName, L"..") != 0)) { /* * Found something in this directory. */ errno = EEXIST; break; } if (FindNextFileW(handle, &data) == FALSE) { break; } } FindClose(handle); } } } } if (errno == ENOTEMPTY) { /* * Posix allows both EEXIST or ENOTEMPTY, but we'll always * return EEXIST to allow easy matching in Erlang code. */ errno = EEXIST; } end: return check_error(-1, errInfo); }