RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, false); AssertReturn(*pszPath, false); Assert(RTPATH_F_IS_VALID(fFlags, 0)); /* * Convert the path and check if it exists using stat(). */ char const *pszNativePath; int rc = rtPathToNative(&pszNativePath, pszPath, NULL); if (RT_SUCCESS(rc)) { struct stat Stat; if (fFlags & RTPATH_F_FOLLOW_LINK) rc = stat(pszNativePath, &Stat); else rc = lstat(pszNativePath, &Stat); if (!rc) rc = VINF_SUCCESS; else rc = VERR_GENERAL_FAILURE; rtPathFreeNative(pszNativePath, pszPath); } return RT_SUCCESS(rc); }
RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, VERR_INVALID_POINTER); AssertReturn(*pszPath, VERR_INVALID_PARAMETER); AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER); AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST, ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs), VERR_INVALID_PARAMETER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); /* * Convert the input path and call common worker. */ HANDLE hRootDir; UNICODE_STRING NtName; int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath); if (RT_SUCCESS(rc)) { rc = rtPathNtQueryInfoWorker(hRootDir, &NtName, pObjInfo, enmAdditionalAttribs, fFlags, pszPath); RTNtPathFree(&NtName, &hRootDir); } return rc; }
RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, false); AssertReturn(*pszPath, false); Assert(RTPATH_F_IS_VALID(fFlags, 0)); /* * Try query file info. */ DWORD dwAttr; PRTUTF16 pwszPath; int rc = RTStrToUtf16(pszPath, &pwszPath); if (RT_SUCCESS(rc)) { dwAttr = GetFileAttributesW(pwszPath); RTUtf16Free(pwszPath); } else dwAttr = INVALID_FILE_ATTRIBUTES; if (dwAttr == INVALID_FILE_ATTRIBUTES) return false; #ifdef FILE_ATTRIBUTE_REPARSE_POINT if ( (fFlags & RTPATH_F_FOLLOW_LINK) && (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)) { AssertFailed(); /** @todo Symlinks: RTPathExists+RTPathExistsEx is misbehaving on symbolic * links on Windows. */ } #endif return true; }
RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, VERR_INVALID_POINTER); AssertReturn(*pszPath, VERR_INVALID_PARAMETER); AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER); AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST, ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs), VERR_INVALID_PARAMETER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); /* * Convert the filename. */ char const *pszNativePath; int rc = rtPathToNative(&pszNativePath, pszPath, NULL); if (RT_SUCCESS(rc)) { struct stat Stat; if (fFlags & RTPATH_F_FOLLOW_LINK) rc = stat(pszNativePath, &Stat); else rc = lstat(pszNativePath, &Stat); /** @todo how doesn't have lstat again? */ if (!rc) { rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0); switch (enmAdditionalAttribs) { case RTFSOBJATTRADD_NOTHING: case RTFSOBJATTRADD_UNIX: Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX); break; case RTFSOBJATTRADD_UNIX_OWNER: rtFsObjInfoAttrSetUnixOwner(pObjInfo, Stat.st_uid); break; case RTFSOBJATTRADD_UNIX_GROUP: rtFsObjInfoAttrSetUnixGroup(pObjInfo, Stat.st_gid); break; case RTFSOBJATTRADD_EASIZE: /** @todo Use SGI extended attribute interface to query EA info. */ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; pObjInfo->Attr.u.EASize.cb = 0; break; default: AssertMsgFailed(("Impossible!\n")); return VERR_INTERNAL_ERROR; } } else rc = RTErrConvertFromErrno(errno); rtPathFreeNative(pszNativePath, pszPath); } LogFlow(("RTPathQueryInfoEx(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n", pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc)); return rc; }
RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, VERR_INVALID_POINTER); AssertReturn(*pszPath, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); /* * Convert the paths. */ char const *pszNativePath; int rc = rtPathToNative(&pszNativePath, pszPath, NULL); if (RT_SUCCESS(rc)) { RTFSOBJINFO ObjInfo; /* * If it's a no-op, we'll only verify the existance of the file. */ if (!pAccessTime && !pModificationTime) rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, fFlags); else { /* * Convert the input to timeval, getting the missing one if necessary, * and call the API which does the change. */ struct timeval aTimevals[2]; if (pAccessTime && pModificationTime) { RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]); RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]); } else { rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags); if (RT_SUCCESS(rc)) { RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]); RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]); } else Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n", pszPath, pAccessTime, pModificationTime, rc)); } if (RT_SUCCESS(rc)) { if (fFlags & RTPATH_F_FOLLOW_LINK) { if (utimes(pszNativePath, aTimevals)) rc = RTErrConvertFromErrno(errno); } #if (defined(RT_OS_DARWIN) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) \ || defined(RT_OS_FREEBSD) \ || defined(RT_OS_LINUX) \ || defined(RT_OS_OS2) /** @todo who really has lutimes? */ else { if (lutimes(pszNativePath, aTimevals)) rc = RTErrConvertFromErrno(errno); } #else else { if (pAccessTime && pModificationTime) rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags); if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode)) rc = VERR_NS_SYMLINK_SET_TIME; else if (RT_SUCCESS(rc)) { if (utimes(pszNativePath, aTimevals)) rc = RTErrConvertFromErrno(errno); } } #endif if (RT_FAILURE(rc)) Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n", pszPath, pAccessTime, pModificationTime, rc, errno)); } } rtPathFreeNative(pszNativePath, pszPath); }
RTDECL(int) RTDirReadEx(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) { int rc; /* * Validate input. */ AssertPtrReturn(pDir, VERR_INVALID_POINTER); AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE); AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER); AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST, VERR_INVALID_PARAMETER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); size_t cbDirEntry = sizeof(*pDirEntry); if (pcbDirEntry) { cbDirEntry = *pcbDirEntry; AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]), ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])), VERR_INVALID_PARAMETER); } /* * Fetch data? */ if (!pDir->fDataUnread) { rc = rtDirNtFetchMore(pDir); if (RT_FAILURE(rc)) return rc; } /* * Convert the filename to UTF-8. */ rc = rtDirNtConvertCurName(pDir); if (RT_FAILURE(rc)) return rc; /* * Check if we've got enough space to return the data. */ const char *pszName = pDir->pszName; const size_t cchName = pDir->cchName; const size_t cbRequired = RT_OFFSETOF(RTDIRENTRYEX, szName[1]) + cchName; if (pcbDirEntry) *pcbDirEntry = cbRequired; if (cbRequired > cbDirEntry) return VERR_BUFFER_OVERFLOW; /* * Setup the returned data. */ PFILE_BOTH_DIR_INFORMATION pBoth = pDir->uCurData.pBoth; pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName); memcpy(pDirEntry->szName, pszName, cchName + 1); memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName)); #ifdef IPRT_WITH_NT_PATH_PASSTHRU if (pDir->enmInfoClass != FileMaximumInformation) #endif { uint8_t cbShort = pBoth->ShortNameLength; if (cbShort > 0) { AssertStmt(cbShort < sizeof(pDirEntry->wszShortName), cbShort = sizeof(pDirEntry->wszShortName) - 2); memcpy(pDirEntry->wszShortName, pBoth->ShortName, cbShort); pDirEntry->cwcShortName = cbShort / 2; } else pDirEntry->cwcShortName = 0; pDirEntry->Info.cbObject = pBoth->EndOfFile.QuadPart; pDirEntry->Info.cbAllocated = pBoth->AllocationSize.QuadPart; Assert(sizeof(uint64_t) == sizeof(pBoth->CreationTime)); RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, pBoth->CreationTime.QuadPart); RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, pBoth->LastAccessTime.QuadPart); RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, pBoth->LastWriteTime.QuadPart); RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, pBoth->ChangeTime.QuadPart); pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pBoth->FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, pszName, cchName); } #ifdef IPRT_WITH_NT_PATH_PASSTHRU else { pDirEntry->cwcShortName = 0; pDirEntry->Info.cbObject = 0; pDirEntry->Info.cbAllocated = 0; RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, 0); RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, 0); RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, 0); RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, 0); if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length, RT_STR_TUPLE("Directory"))) pDirEntry->Info.Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777; else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length, RT_STR_TUPLE("SymbolicLink"))) pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777; else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length, RT_STR_TUPLE("Device"))) pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666; else pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666; } #endif /* * Requested attributes (we cannot provide anything actually). */ switch (enmAdditionalAttribs) { case RTFSOBJATTRADD_EASIZE: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; #ifdef IPRT_WITH_NT_PATH_PASSTHRU if (pDir->enmInfoClass == FileMaximumInformation) pDirEntry->Info.Attr.u.EASize.cb = 0; else #endif pDirEntry->Info.Attr.u.EASize.cb = pBoth->EaSize; break; case RTFSOBJATTRADD_UNIX: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX; pDirEntry->Info.Attr.u.Unix.uid = ~0U; pDirEntry->Info.Attr.u.Unix.gid = ~0U; pDirEntry->Info.Attr.u.Unix.cHardlinks = 1; pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */ pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */ pDirEntry->Info.Attr.u.Unix.fFlags = 0; pDirEntry->Info.Attr.u.Unix.GenerationId = 0; pDirEntry->Info.Attr.u.Unix.Device = 0; break; case RTFSOBJATTRADD_NOTHING: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING; break; case RTFSOBJATTRADD_UNIX_OWNER: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER; pDirEntry->Info.Attr.u.UnixOwner.uid = ~0U; pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */ break; case RTFSOBJATTRADD_UNIX_GROUP: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP; pDirEntry->Info.Attr.u.UnixGroup.gid = ~0U; pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0'; break; default: AssertMsgFailed(("Impossible!\n")); return VERR_INTERNAL_ERROR; } /* * Follow links if requested. */ if ( (fFlags & RTPATH_F_FOLLOW_LINK) && RTFS_IS_SYMLINK(fFlags)) { /** @todo Symlinks: Find[First|Next]FileW will return info about the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */ } /* * Finally advance the buffer. */ return rtDirNtAdvanceBuffer(pDir); }
RTDECL(int) RTDirReadEx(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) { /** @todo Symlinks: Find[First|Next]FileW will return info about the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */ /* * Validate input. */ if (!pDir || pDir->u32Magic != RTDIR_MAGIC) { AssertMsgFailed(("Invalid pDir=%p\n", pDir)); return VERR_INVALID_PARAMETER; } if (!pDirEntry) { AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry)); return VERR_INVALID_PARAMETER; } if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING || enmAdditionalAttribs > RTFSOBJATTRADD_LAST) { AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs)); return VERR_INVALID_PARAMETER; } AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); size_t cbDirEntry = sizeof(*pDirEntry); if (pcbDirEntry) { cbDirEntry = *pcbDirEntry; if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName[2])) { AssertMsgFailed(("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2]))); return VERR_INVALID_PARAMETER; } } /* * Fetch data? */ if (!pDir->fDataUnread) { RTStrFree(pDir->pszName); pDir->pszName = NULL; BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data); if (!fRc) { int iErr = GetLastError(); if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES) return VERR_NO_MORE_FILES; return RTErrConvertFromWin32(iErr); } } /* * Convert the filename to UTF-8. */ if (!pDir->pszName) { int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName); if (RT_FAILURE(rc)) { pDir->pszName = NULL; return rc; } pDir->cchName = strlen(pDir->pszName); } /* * Check if we've got enough space to return the data. */ const char *pszName = pDir->pszName; const size_t cchName = pDir->cchName; const size_t cbRequired = RT_OFFSETOF(RTDIRENTRYEX, szName[1]) + cchName; if (pcbDirEntry) *pcbDirEntry = cbRequired; if (cbRequired > cbDirEntry) return VERR_BUFFER_OVERFLOW; /* * Setup the returned data. */ pDir->fDataUnread = false; pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName); memcpy(pDirEntry->szName, pszName, cchName + 1); if (pDir->Data.cAlternateFileName[0]) { /* copy and calc length */ PCRTUTF16 pwszSrc = (PCRTUTF16)pDir->Data.cAlternateFileName; PRTUTF16 pwszDst = pDirEntry->wszShortName; uint32_t off = 0; while (off < RT_ELEMENTS(pDirEntry->wszShortName) - 1U && pwszSrc[off]) { pwszDst[off] = pwszSrc[off]; off++; } pDirEntry->cwcShortName = (uint16_t)off; /* zero the rest */ do pwszDst[off++] = '\0'; while (off < RT_ELEMENTS(pDirEntry->wszShortName)); } else { memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName)); pDirEntry->cwcShortName = 0; } pDirEntry->Info.cbObject = ((uint64_t)pDir->Data.nFileSizeHigh << 32) | (uint64_t)pDir->Data.nFileSizeLow; pDirEntry->Info.cbAllocated = pDirEntry->Info.cbObject; Assert(sizeof(uint64_t) == sizeof(pDir->Data.ftCreationTime)); RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, *(uint64_t *)&pDir->Data.ftCreationTime); RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, *(uint64_t *)&pDir->Data.ftLastAccessTime); RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, *(uint64_t *)&pDir->Data.ftLastWriteTime); pDirEntry->Info.ChangeTime = pDirEntry->Info.ModificationTime; pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pDir->Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, pszName, cchName); /* * Requested attributes (we cannot provide anything actually). */ switch (enmAdditionalAttribs) { case RTFSOBJATTRADD_EASIZE: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; pDirEntry->Info.Attr.u.EASize.cb = 0; break; case RTFSOBJATTRADD_UNIX: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX; pDirEntry->Info.Attr.u.Unix.uid = ~0U; pDirEntry->Info.Attr.u.Unix.gid = ~0U; pDirEntry->Info.Attr.u.Unix.cHardlinks = 1; pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */ pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */ pDirEntry->Info.Attr.u.Unix.fFlags = 0; pDirEntry->Info.Attr.u.Unix.GenerationId = 0; pDirEntry->Info.Attr.u.Unix.Device = 0; break; case RTFSOBJATTRADD_NOTHING: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING; break; case RTFSOBJATTRADD_UNIX_OWNER: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER; pDirEntry->Info.Attr.u.UnixOwner.uid = ~0U; pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */ break; case RTFSOBJATTRADD_UNIX_GROUP: pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP; pDirEntry->Info.Attr.u.UnixGroup.gid = ~0U; pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0'; break; default: AssertMsgFailed(("Impossible!\n")); return VERR_INTERNAL_ERROR; } return VINF_SUCCESS; }
RTDECL(int) RTDirReadEx(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) { /* * Validate and digest input. */ if (!rtDirValidHandle(pDir)) return VERR_INVALID_PARAMETER; AssertMsgReturn(VALID_PTR(pDirEntry), ("%p\n", pDirEntry), VERR_INVALID_POINTER); AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST, ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs), VERR_INVALID_PARAMETER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); size_t cbDirEntry = sizeof(*pDirEntry); if (pcbDirEntry) { AssertMsgReturn(VALID_PTR(pcbDirEntry), ("%p\n", pcbDirEntry), VERR_INVALID_POINTER); cbDirEntry = *pcbDirEntry; AssertMsgReturn(cbDirEntry >= (unsigned)RT_OFFSETOF(RTDIRENTRYEX, szName[2]), ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])), VERR_INVALID_PARAMETER); } /* * Fetch more data if necessary and/or convert the name. */ int rc = rtDirReadMore(pDir); if (RT_SUCCESS(rc)) { /* * Check if we've got enough space to return the data. */ const char *pszName = pDir->pszName; const size_t cchName = pDir->cchName; const size_t cbRequired = RT_OFFSETOF(RTDIRENTRYEX, szName[1]) + cchName; if (pcbDirEntry) *pcbDirEntry = cbRequired; if (cbRequired <= cbDirEntry) { /* * Setup the returned data. */ pDirEntry->cwcShortName = 0; pDirEntry->wszShortName[0] = 0; pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName); memcpy(pDirEntry->szName, pszName, cchName + 1); /* get the info data */ size_t cch = cchName + pDir->cchPath + 1; char *pszNamePath = (char *)alloca(cch); if (pszNamePath) { memcpy(pszNamePath, pDir->pszPath, pDir->cchPath); memcpy(pszNamePath + pDir->cchPath, pszName, cchName + 1); rc = RTPathQueryInfoEx(pszNamePath, &pDirEntry->Info, enmAdditionalAttribs, fFlags); } else rc = VERR_NO_MEMORY; if (RT_FAILURE(rc)) { #ifdef HAVE_DIRENT_D_TYPE rtDirSetDummyInfo(&pDirEntry->Info, rtDirType(pDir->Data.d_type)); #else rtDirSetDummyInfo(&pDirEntry->Info, RTDIRENTRYTYPE_UNKNOWN); #endif rc = VWRN_NO_DIRENT_INFO; } /* free cached data */ pDir->fDataUnread = false; rtPathFreeIprt(pDir->pszName, pDir->Data.d_name); pDir->pszName = NULL; } else rc = VERR_BUFFER_OVERFLOW; } return rc; }
RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, VERR_INVALID_POINTER); AssertReturn(*pszPath, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); /* * Convert the path. */ PRTUTF16 pwszPath; int rc = RTStrToUtf16(pszPath, &pwszPath); if (RT_SUCCESS(rc)) { HANDLE hFile; if (fFlags & RTPATH_F_FOLLOW_LINK) hFile = CreateFileW(pwszPath, FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */ NULL, /* security attribs */ OPEN_EXISTING, /* dwCreationDisposition */ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, NULL); else { /** @todo Symlink: Test RTPathSetTimesEx on Windows. (The code is disabled * because it's not tested yet.) */ #if 0 //def FILE_FLAG_OPEN_REPARSE_POINT hFile = CreateFileW(pwszPath, FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */ NULL, /* security attribs */ OPEN_EXISTING, /* dwCreationDisposition */ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) #endif hFile = CreateFileW(pwszPath, FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */ NULL, /* security attribs */ OPEN_EXISTING, /* dwCreationDisposition */ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, NULL); } if (hFile != INVALID_HANDLE_VALUE) { /* * Check if it's a no-op. */ if (!pAccessTime && !pModificationTime && !pBirthTime) rc = VINF_SUCCESS; /* NOP */ else { /* * Convert the input and call the API. */ FILETIME CreationTimeFT; PFILETIME pCreationTimeFT = NULL; if (pBirthTime) pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT); FILETIME LastAccessTimeFT; PFILETIME pLastAccessTimeFT = NULL; if (pAccessTime) pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT); FILETIME LastWriteTimeFT; PFILETIME pLastWriteTimeFT = NULL; if (pModificationTime) pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT); if (SetFileTime(hFile, pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT)) rc = VINF_SUCCESS; else { DWORD Err = GetLastError(); rc = RTErrConvertFromWin32(Err); Log(("RTPathSetTimes('%s', %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n", pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc)); } } BOOL fRc = CloseHandle(hFile); Assert(fRc); NOREF(fRc); } else { DWORD Err = GetLastError(); rc = RTErrConvertFromWin32(Err); Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and lasterr=%u\n", pszPath, rc, Err)); } RTUtf16Free(pwszPath); } LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n", pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime, pChangeTime, pChangeTime, pBirthTime, pBirthTime)); return rc; }
RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, VERR_INVALID_POINTER); AssertReturn(*pszPath, VERR_INVALID_PARAMETER); AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER); AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST, ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs), VERR_INVALID_PARAMETER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); /* * Query file info. */ WIN32_FILE_ATTRIBUTE_DATA Data; PRTUTF16 pwszPath; int rc = RTStrToUtf16(pszPath, &pwszPath); if (RT_FAILURE(rc)) return rc; if (!GetFileAttributesExW(pwszPath, GetFileExInfoStandard, &Data)) { /* Fallback to FindFileFirst in case of sharing violation. */ if (GetLastError() == ERROR_SHARING_VIOLATION) { WIN32_FIND_DATAW FindData; HANDLE hDir = FindFirstFileW(pwszPath, &FindData); if (hDir == INVALID_HANDLE_VALUE) { rc = RTErrConvertFromWin32(GetLastError()); RTUtf16Free(pwszPath); return rc; } FindClose(hDir); Data.dwFileAttributes = FindData.dwFileAttributes; Data.ftCreationTime = FindData.ftCreationTime; Data.ftLastAccessTime = FindData.ftLastAccessTime; Data.ftLastWriteTime = FindData.ftLastWriteTime; Data.nFileSizeHigh = FindData.nFileSizeHigh; Data.nFileSizeLow = FindData.nFileSizeLow; } else { rc = RTErrConvertFromWin32(GetLastError()); RTUtf16Free(pwszPath); return rc; } } /* * Getting the information for the link target is a bit annoying and * subject to the same access violation mess as above.. :/ */ /** @todo we're too lazy wrt to error paths here... */ if ( (fFlags & RTPATH_F_FOLLOW_LINK) && (Data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { HANDLE hFinal = CreateFileW(pwszPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFinal != INVALID_HANDLE_VALUE) { BY_HANDLE_FILE_INFORMATION FileData; if (GetFileInformationByHandle(hFinal, &FileData)) { Data.dwFileAttributes = FileData.dwFileAttributes; Data.ftCreationTime = FileData.ftCreationTime; Data.ftLastAccessTime = FileData.ftLastAccessTime; Data.ftLastWriteTime = FileData.ftLastWriteTime; Data.nFileSizeHigh = FileData.nFileSizeHigh; Data.nFileSizeLow = FileData.nFileSizeLow; } CloseHandle(hFinal); } else if (GetLastError() != ERROR_SHARING_VIOLATION) { rc = RTErrConvertFromWin32(GetLastError()); RTUtf16Free(pwszPath); return rc; } } RTUtf16Free(pwszPath); /* * Setup the returned data. */ pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32) | (uint64_t)Data.nFileSizeLow; pObjInfo->cbAllocated = pObjInfo->cbObject; Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime)); RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime); RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime); RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime); pObjInfo->ChangeTime = pObjInfo->ModificationTime; pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, pszPath, strlen(pszPath)); /* * Requested attributes (we cannot provide anything actually). */ switch (enmAdditionalAttribs) { case RTFSOBJATTRADD_NOTHING: pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING; break; case RTFSOBJATTRADD_UNIX: pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX; pObjInfo->Attr.u.Unix.uid = ~0U; pObjInfo->Attr.u.Unix.gid = ~0U; pObjInfo->Attr.u.Unix.cHardlinks = 1; pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /** @todo use volume serial number */ pObjInfo->Attr.u.Unix.INodeId = 0; /** @todo use fileid (see GetFileInformationByHandle). */ pObjInfo->Attr.u.Unix.fFlags = 0; pObjInfo->Attr.u.Unix.GenerationId = 0; pObjInfo->Attr.u.Unix.Device = 0; break; case RTFSOBJATTRADD_UNIX_OWNER: pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER; pObjInfo->Attr.u.UnixOwner.uid = ~0U; pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */ break; case RTFSOBJATTRADD_UNIX_GROUP: pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP; pObjInfo->Attr.u.UnixGroup.gid = ~0U; pObjInfo->Attr.u.UnixGroup.szName[0] = '\0'; break; case RTFSOBJATTRADD_EASIZE: pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; pObjInfo->Attr.u.EASize.cb = 0; break; default: AssertMsgFailed(("Impossible!\n")); return VERR_INTERNAL_ERROR; } return VINF_SUCCESS; }