📄 path.c
字号:
* EXIT
* Attributes of file/subdirectory if it exists, 0xFFFFFFFF if it
* does not (call GetLastError for error code).
*/
DWORD FAT_GetFileAttributesW(PVOLUME pvol, PCWSTR pwsFileName)
{
SHANDLE sh;
WIN32_FIND_DATAW fd;
WCHAR wsFName[MAX_PATH+1];
DEBUGMSGW(ZONE_APIS,(DBGTEXTW("FATFS!FAT_GetFileAttributesW(%d chars: %s)\r\n"), wcslen(pwsFileName), pwsFileName));
if (!FATEnter(NULL, LOGID_NONE))
return INVALID_ATTR;
if (pvol->v_flags & (VOLF_INVALID | VOLF_UNMOUNTED | VOLF_FROZEN | VOLF_WRITELOCKED)) {
SetLastError(ERROR_ACCESS_DENIED);
FATExit(pvol, LOGID_NONE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS,(DBGTEXTW("FATFS!FAT_GetFileAttributesW(%-.64s) returned 0x%x (%d)\r\n"), pwsFileName, INVALID_ATTR, ERROR_ACCESS_DENIED));
return INVALID_ATTR;
}
// FindFirst will call SetLastError appropriately, so
// all we have to do is bail if it doesn't return a stream.
wcsncpy (wsFName, pwsFileName, MAX_PATH);
wsFName[MAX_PATH] = TEXT('\0'); // Ensure null
// Desktop will remove trailing '\' && '/'
while (wcslen(wsFName) && ((wsFName[wcslen(wsFName)-1] == TEXT('\\')) ||
(wsFName[wcslen(wsFName)-1] == TEXT('/')))) {
// Trim trailing slash
wsFName[wcslen(wsFName)-1] = TEXT('\0');
}
sh.sh_flags = SHF_BYNAME;
FindFirst(pvol, wsFName, &sh, NULL, &fd, NAME_FILE | NAME_DIR, UNKNOWN_CLUSTER);
FATExit(pvol, LOGID_NONE);
DEBUGMSGW(ZONE_APIS,(DBGTEXTW("FATFS!FAT_GetFileAttributesW(%-.64s) returned 0x%x (%d)\r\n"), pwsFileName, fd.dwFileAttributes, fd.dwFileAttributes != INVALID_ATTR? 0 : GetLastError()));
return fd.dwFileAttributes;
}
/* FAT_SetFileAttributesW - Set file/subdirectory attributes
*
* ENTRY
* pvol - pointer to VOLUME
* pwsFileName - pointer to name of existing file/subdirectory
* dwAttributes - new attributes for file/subdirectory
*
* EXIT
* TRUE if successful, FALSE if not (call GetLastError for error code)
*/
BOOL FAT_SetFileAttributesW(PVOLUME pvol, PCWSTR pwsFileName, DWORD dwAttributes)
{
PDSTREAM pstmDir;
SHANDLE sh;
DIRINFO di;
DWORD dw, dwError = ERROR_SUCCESS;
WIN32_FIND_DATAW fd;
DWORD dwSHCNE = 0;
DSID sidParent;
DEBUGMSGW(ZONE_APIS,(DBGTEXTW("FATFS!FAT_SetFileAttributesW(0x%x,%d chars: %s)\r\n"), dwAttributes, wcslen(pwsFileName), pwsFileName));
// NOTE: Even though this call modifies the volume, it never has any impact
// on file system integrity, so we don't waste time logging it (ie, LOGID_NONE).
if (!FATEnter(NULL, LOGID_NONE))
return FALSE;
if (pvol->v_flags & (VOLF_INVALID | VOLF_UNMOUNTED | VOLF_FROZEN | VOLF_LOCKED)) {
dwError = ERROR_ACCESS_DENIED;
goto error;
}
if (pvol->v_flags & VOLF_READONLY) {
dwError = ERROR_WRITE_PROTECT;
goto error;
}
// FindFirst will call SetLastError appropriately, so
// all we have to do is bail if it doesn't return a stream.
sh.sh_flags = SHF_BYNAME;
pstmDir = FindFirst(pvol, pwsFileName, &sh, &di, &fd, NAME_FILE | NAME_DIR, UNKNOWN_CLUSTER);
if (!pstmDir) {
FATExit(pvol, LOGID_NONE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS,(DBGTEXTW("FATFS!FAT_SetFileAttributesW(%-.64s) returned FALSE (%d)\r\n"), pwsFileName, GetLastError()));
return FALSE;
}
// If FindFirst was successful...
if (fd.dwFileAttributes != INVALID_ATTR) {
// Clear any bits that are not changable
dwAttributes &= ATTR_CHANGEABLE;
// NOTE: We pass 0 for the size instead of sizeof(DIRENTRY) to disable logging
// of this modification, since we are not logging these particular calls (by virtue
// of LOGID_NONE above).
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
dwAttributes |= FILE_ATTRIBUTE_DIRECTORY;
}
#ifdef TFAT
if (pvol->v_fTfat && (pvol->v_flFATFS & FATFS_TFAT_NONATOMIC_SECTOR)) {
// we need to clone the current buffer
// before we make any modifications!
LockFAT(pvol);
if (CLONE_CLUSTER_COMPLETE == CloneDirCluster( pvol, pstmDir, pstmDir->s_pbufCur->b_blk, NULL ))
{
// The current buffer might be changed in CloneDirCluster
// update plde to with the new buffer
dwError = ReadStream( pstmDir, di.di_pos, &di.di_pde, NULL);
}
}
#endif
if (!dwError)
dwError = ModifyStreamBuffer(pstmDir, di.di_pde, 0);
if (!dwError)
di.di_pde->de_attr = (BYTE)dwAttributes;
dwSHCNE = SHCNE_UPDATEITEM;
memcpy (&sidParent, &pstmDir->s_sid, sizeof(DSID));
#ifdef TFAT
if (pvol->v_fTfat && (pvol->v_flFATFS & FATFS_TFAT_NONATOMIC_SECTOR)) {
if (!dwError)
dwError = CommitStreamBuffers (pstmDir);
if (!dwError)
dwError = CommitTransactions (pvol);
UnlockFAT(pvol);
}
#endif
}
dw = CloseStream(pstmDir);
if (!dwError)
dwError = dw;
if (dwSHCNE)
FILESYSTEMNOTIFICATION(pvol, DB_CEOID_CHANGED, 0, dwSHCNE, &di.di_sid, &sidParent, NULL, NULL, NULL, DBGTEXTW("FAT_SetFileAttributesW"));
if (dwError) {
error:
SetLastError(dwError);
}
FATExit(pvol, LOGID_NONE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS && dwError,(DBGTEXTW("FATFS!FAT_SetFileAttributesW(0x%x,%-.64s) returned 0x%x (%d)\r\n"), dwAttributes, pwsFileName, dwError == ERROR_SUCCESS, dwError));
return dwError == ERROR_SUCCESS;
}
/* FAT_DeleteFileW - Delete file
*
* ENTRY
* pvol - pointer to VOLUME
* pwsFileName - pointer to name of existing file
*
* EXIT
* TRUE if successful, FALSE if not (call GetLastError for error code)
*
* NOTES
* A file marked FILE_ATTRIBUTE_READONLY cannot be deleted. You have to
* remove that attribute first, with SetFileAttributes.
*
* An open file cannot be deleted. All open handles must be closed first.
*
* A subdirectory cannot be deleted with this call either. You have to
* use RemoveDirectory instead.
*/
BOOL FAT_DeleteFileW(PVOLUME pvol, PCWSTR pwsFileName)
{
PDSTREAM pstmDir;
SHANDLE sh;
DIRINFO di;
WIN32_FIND_DATAW fd;
DWORD dwError = ERROR_SUCCESS;
#ifdef SHELL_CALLBACK_NOTIFICATION
CEOIDINFO oiOld;
#endif
DSID sidParent;
DEBUGMSGW(ZONE_APIS,(DBGTEXTW("FATFS!FAT_DeleteFileW(%d chars: %s)\r\n"), wcslen(pwsFileName), pwsFileName));
if (!FATEnter(pvol, LOGID_DELETEFILE))
return FALSE;
if (pvol->v_flags & (VOLF_INVALID | VOLF_UNMOUNTED | VOLF_FROZEN | VOLF_LOCKED)) {
dwError = ERROR_ACCESS_DENIED;
goto error;
}
if (pvol->v_flags & VOLF_READONLY) {
dwError = ERROR_WRITE_PROTECT;
goto error;
}
// FindFirst will call SetLastError appropriately, so
// all we have to do is bail if it doesn't return a stream.
sh.sh_flags = SHF_BYNAME;
pstmDir = FindFirst(pvol, pwsFileName, &sh, &di, &fd, NAME_FILE, UNKNOWN_CLUSTER);
if (!pstmDir) {
FATExit(pvol, LOGID_DELETEFILE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS,(DBGTEXTW("FATFS!FAT_DeleteFileW(%-.64s) returned FALSE (%d)\r\n"), pwsFileName, GetLastError()));
return FALSE;
}
// If the source file doesn't exist, fail
if (fd.dwFileAttributes == INVALID_ATTR) {
CloseStream(pstmDir);
FATExit(pvol, LOGID_DELETEFILE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS,(DBGTEXTW("FATFS!FAT_DeleteFileW(%-.64s) returned FALSE (%d)\r\n"), pwsFileName, GetLastError()));
return FALSE;
}
// If the source file is R/O or a directory, fail
//
// NOTE: Win95 reports ERROR_FILE_NOT_FOUND if the name is an existing
// directory, whereas NT reports ERROR_ACCESS_DENIED. I find the latter
// to be more sensible. -JTP
if (fd.dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY)) {
dwError = ERROR_ACCESS_DENIED;
goto exit;
}
// If the source file has any open handles, fail
if (CheckStreamHandles(pstmDir->s_pvol, &di.di_sid)) {
dwError = ERROR_SHARING_VIOLATION;
goto exit;
}
// Retrieve the fully-qualified pathname of the source, in case we need
// it for the FILESYSTEMNOTIFICATION at the end of the function. We have to
// do the wacky buffer holding/unholding because pstmDir's current buffer is
// in a known state, and we can't tolerate GetSIDInfo mucking that state up.
#ifdef SHELL_CALLBACK_NOTIFICATION
if (pfnShell && ZONE_SHELLMSGS) {
PBUF pbuf = pstmDir->s_pbufCur;
HoldBuffer(pbuf);
GetSIDInfo(pvol, &di.di_sid, &oiOld);
ReleaseStreamBuffer(pstmDir, FALSE);
AssignStreamBuffer(pstmDir, pbuf, FALSE);
UnholdBuffer(pbuf);
}
#endif
dwError = DestroyName(pstmDir, &di);
// TEST_BREAK
PWR_BREAK_NOTIFY(31);
if (!dwError)
memcpy (&sidParent, &pstmDir->s_sid, sizeof (DSID));
exit:
CloseStream(pstmDir);
if (!dwError)
FILESYSTEMNOTIFICATION(pvol, 0, DB_CEOID_FILE_DELETED, SHCNE_DELETE, NULL, NULL, &di.di_sid, &sidParent, &oiOld, DBGTEXTW("FAT_DeleteFileW"));
if (dwError) {
error:
SetLastError(dwError);
}
#ifdef TFAT
else if (pvol->v_fTfat)
dwError = CommitTransactions (pvol);
#endif
FATExit(pvol, LOGID_DELETEFILE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS && dwError,(DBGTEXTW("FATFS!FAT_DeleteFileW(%-.64s) returned 0x%x (%d)\r\n"), pwsFileName, dwError == ERROR_SUCCESS, dwError));
return dwError == ERROR_SUCCESS;
}
/* FAT_MoveFileW
*
* ENTRY
* pvol - pointer to VOLUME
* pwsOldFileName - pointer to name of existing file
* pwsNewFileName - pointer to new name for file
*
* EXIT
* TRUE if successful, FALSE if not (call GetLastError for error code)
*
* NOTES
* We call FindFirst once to obtain the source directory stream for the
* for the existing file, and if it really exists, we call FindFirst
* again to obtain the destination directory stream for the new file,
* verifying that the new name does NOT exist. Then we create the new
* name and destroy the old.
*
* When moving a directory, we must make sure that our traversal
* of the destination path does not cross the source directory, otherwise
* we will end up creating a circular directory chain.
*/
BOOL FAT_MoveFileW(PVOLUME pvol, PCWSTR pwsOldFileName, PCWSTR pwsNewFileName)
{
PBUF pbufSrc;
BYTE bAttrSrc;
WIN32_FIND_DATAW fd;
SHANDLE shSrc, shDst;
DIRINFO diSrc, diDst;
PDSTREAM pstmSrc, pstmDst;
DIRENTRY deClone, *pdeClone;
DWORD dwError = ERROR_SUCCESS;
DWORD clusFail = UNKNOWN_CLUSTER;
#ifdef SHELL_CALLBACK_NOTIFICATION
CEOIDINFO oiOld;
#endif
DSID sidSrcParent, sidDstParent;
DEBUGONLY(pdeClone = INVALID_PTR);
DEBUGMSGW(ZONE_APIS,(DBGTEXTW("FATFS!FAT_MoveFileW(%d->%d chars: %s->%s)\r\n"), wcslen(pwsOldFileName), wcslen(pwsNewFileName), pwsOldFileName, pwsNewFileName));
if (!FATEnter(pvol, LOGID_MOVEFILE))
return FALSE;
if (pvol->v_flags & (VOLF_INVALID | VOLF_UNMOUNTED | VOLF_FROZEN | VOLF_LOCKED)) {
dwError = ERROR_ACCESS_DENIED;
goto error;
}
if (pvol->v_flags & VOLF_READONLY) {
dwError = ERROR_WRITE_PROTECT;
goto error;
}
// We add an explicit call to OpenRoot because this function performs
// two path traversals starting at the root, with a lock on the source
// directory while it traverses the destination. So if another thread
// tried to open a file in that source directory at the same time, it would
// block waiting for MoveFile to finish, but MoveFile couldn't finish if
// the other thread currently had a stream open in MoveFile's destination
// path. The explicit OpenRoot prevents all other path-based calls from
// even starting down that path.
OpenRoot(pvol);
shSrc.sh_flags = SHF_BYNAME;
pstmSrc = FindFirst(pvol, pwsOldFileName, &shSrc, &diSrc, &fd, NAME_FILE | NAME_DIR, UNKNOWN_CLUSTER);
if (!pstmSrc) {
CloseRoot(pvol);
FATExit(pvol, LOGID_MOVEFILE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS,(DBGTEXTW("FATFS!FAT_MoveFileW(%s->%s) returned FALSE (%d)\r\n"), pwsOldFileName, pwsNewFileName, GetLastError()));
return FALSE;
}
DEBUGMSGW(ZONE_APIS,(DBGTEXTW("FATFS!FAT_MoveFileW. Source (%s) located at cluster %d\r\n"), pwsOldFileName, diSrc.di_clusEntry));
// If the source file doesn't exist, fail
if (fd.dwFileAttributes == INVALID_ATTR) {
CloseStream(pstmSrc);
CloseRoot(pvol);
FATExit(pvol, LOGID_MOVEFILE);
DEBUGMSGW(ZONE_APIS || ZONE_ERRORS,(DBGTEXTW("FATFS!FAT_MoveFileW(%s->%s) returned FALSE (%d)\r\n"), pwsOldFileName, pwsNewFileName, GetLastError()));
return FALSE;
}
// If the source file has any open handles, fail
if (StreamOpenedForExclAccess(pstmSrc->s_pvol, &diSrc.di_sid)) {
dwError = ERROR_SHARING_VIOLATION;
goto exit3;
}
bAttrSrc = (BYTE)fd.dwFileAttributes;
#ifdef TFAT
if (pvol->v_fTfat && (pvol->v_flFATFS & FATFS_TFAT_DISABLE_MOVEDIR) && (bAttrSrc & ATTR_DIRECTORY)) {
// Moving a directory has been disabled on this volume
dwError = ERROR_ACCESS_DENIED;
goto exit3;
}
#endif
// Retrieve the fully-qualified pathname of the source, in case we need
// it for the FILESYSTEMNOTIFICATION at the end of the function. We have to
// do the wacky buffer holding/unholding because pstmSrc's current buffer is
// in a known state, and we can't tolerate GetSIDInfo mucking that state up.
#ifdef SHELL_CALLBACK_NOTIFICATION
if (pfnShell && ZONE_SHELLMSGS) {
PBUF pbuf = pstmSrc->s_pbufCur;
HoldBuffer(pbuf);
GetSIDInfo(pvol, &diSrc.di_sid, &oiOld);
ReleaseStreamBuffer(pstmSrc, FALSE);
AssignStreamBuffer(pstmSrc, pbuf, FALSE);
UnholdBuffer(pbuf);
}
#endif
// We don't need the source pattern anymore, but we *might* need
// the complete source filename (if we had to delete the source first,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -