Changeset 66671 in vbox
- Timestamp:
- Apr 25, 2017 11:58:04 AM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp
r66670 r66671 415 415 * Internal Functions * 416 416 *********************************************************************************************************************************/ 417 static int rtFsFatFile_FlushMetaData(PRTFSFATFILE pThis); 417 418 static void rtFsFatDir_AddOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild); 418 419 static void rtFsFatDir_RemoveOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild); 420 static int rtFsFatDir_GetEntryForUpdate(PRTFSFATDIR pThis, uint32_t offEntryInDir, PFATDIRENTRY *ppDirEntry, uint32_t *puWriteLock); 421 static int rtFsFatDir_PutEntryAfterUpdate(PRTFSFATDIR pThis, PFATDIRENTRY pDirEntry, uint32_t uWriteLock); 419 422 static int rtFsFatDir_Flush(PRTFSFATDIR pThis); 420 423 static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir, … … 808 811 AssertReturn(pFatCache->aEntries[0].offFat == 0, VERR_INTERNAL_ERROR_4); 809 812 813 /* Special case for empty files. */ 814 if (idxCluster == 0) 815 return VINF_SUCCESS; 816 817 /* Work cluster by cluster. */ 810 818 uint8_t const *pbFat = pFatCache->aEntries[0].pbData; 811 819 for (;;) … … 882 890 default: 883 891 AssertFailedReturn(VERR_INTERNAL_ERROR_2); 892 } 893 } 894 895 896 /** Sets a FAT12 cluster value. */ 897 static int rtFsFatClusterMap_SetCluster12(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, 898 uint32_t idxCluster, uint32_t uValue) 899 { 900 /* ASSUME that for FAT12 we cache the whole FAT in a single entry. That 901 way we don't need to deal with entries in different sectors and whatnot. */ 902 AssertReturn(pFatCache->cEntries == 1, VERR_INTERNAL_ERROR_4); 903 AssertReturn(pFatCache->cbEntry == pVol->cbFat, VERR_INTERNAL_ERROR_4); 904 AssertReturn(pFatCache->aEntries[0].offFat == 0, VERR_INTERNAL_ERROR_4); 905 AssertReturn(uValue < 0x1000, VERR_INTERNAL_ERROR_2); 906 907 /* Make the change. */ 908 uint8_t *pbFat = pFatCache->aEntries[0].pbData; 909 uint32_t offFat = idxCluster * 3 / 2; 910 if (idxCluster & 1) 911 { 912 pbFat[offFat] = (0x0f | pbFat[offFat]) | ((uValue & 0xf) << 4); 913 pbFat[offFat + 1] = (uint8_t)(uValue >> 4); 914 } 915 else 916 { 917 pbFat[offFat] = (uint8_t)uValue; 918 pbFat[offFat + 1] = (0xf0 | pbFat[offFat + 1]) | ((uValue >> 8) | 0xf); 919 } 920 921 /* Update the dirty bits. */ 922 pFatCache->aEntries[0].bmDirty |= RT_BIT_64(offFat >> pFatCache->cDirtyShift); 923 pFatCache->aEntries[0].bmDirty |= RT_BIT_64((offFat + 1) >> pFatCache->cDirtyShift); 924 925 return VINF_SUCCESS; 926 } 927 928 929 /** Sets a FAT16 cluster value. */ 930 static int rtFsFatClusterMap_SetCluster16(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, 931 uint32_t idxCluster, uint32_t uValue) 932 { 933 AssertReturn(uValue < 0x10000, VERR_INTERNAL_ERROR_2); 934 RT_NOREF(pFatCache, pVol, idxCluster, uValue); 935 return VERR_NOT_IMPLEMENTED; 936 } 937 938 939 /** Sets a FAT32 cluster value. */ 940 static int rtFsFatClusterMap_SetCluster32(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, 941 uint32_t idxCluster, uint32_t uValue) 942 { 943 AssertReturn(uValue < 0x10000000, VERR_INTERNAL_ERROR_2); 944 RT_NOREF(pFatCache, pVol, idxCluster, uValue); 945 return VERR_NOT_IMPLEMENTED; 946 } 947 948 949 /** 950 * Marks the cluster @a idxCluster as the end of the cluster chain. 951 * 952 * @returns IPRT status code 953 * @param pThis The FAT volume instance. 954 * @param idxCluster The cluster to end the chain with. 955 */ 956 static int rtFsFatClusterMap_SetEndOfChain(PRTFSFATVOL pThis, uint32_t idxCluster) 957 { 958 AssertReturn(idxCluster >= FAT_FIRST_DATA_CLUSTER, VERR_VFS_BOGUS_OFFSET); 959 AssertReturn(idxCluster < pThis->cClusters, VERR_VFS_BOGUS_OFFSET); 960 switch (pThis->enmFatType) 961 { 962 case RTFSFATTYPE_FAT12: return rtFsFatClusterMap_SetCluster12(pThis->pFatCache, pThis, idxCluster, FAT_FIRST_FAT12_EOC); 963 case RTFSFATTYPE_FAT16: return rtFsFatClusterMap_SetCluster16(pThis->pFatCache, pThis, idxCluster, FAT_FIRST_FAT16_EOC); 964 case RTFSFATTYPE_FAT32: return rtFsFatClusterMap_SetCluster32(pThis->pFatCache, pThis, idxCluster, FAT_FIRST_FAT32_EOC); 965 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3); 966 } 967 } 968 969 970 /** 971 * Marks the cluster @a idxCluster as free. 972 * @returns IPRT status code 973 * @param pThis The FAT volume instance. 974 * @param idxCluster The cluster to free. 975 */ 976 static int rtFsFatClusterMap_FreeCluster(PRTFSFATVOL pThis, uint32_t idxCluster) 977 { 978 AssertReturn(idxCluster >= FAT_FIRST_DATA_CLUSTER, VERR_VFS_BOGUS_OFFSET); 979 AssertReturn(idxCluster < pThis->cClusters, VERR_VFS_BOGUS_OFFSET); 980 switch (pThis->enmFatType) 981 { 982 case RTFSFATTYPE_FAT12: return rtFsFatClusterMap_SetCluster12(pThis->pFatCache, pThis, idxCluster, 0); 983 case RTFSFATTYPE_FAT16: return rtFsFatClusterMap_SetCluster16(pThis->pFatCache, pThis, idxCluster, 0); 984 case RTFSFATTYPE_FAT32: return rtFsFatClusterMap_SetCluster32(pThis->pFatCache, pThis, idxCluster, 0); 985 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3); 884 986 } 885 987 } … … 995 1097 { 996 1098 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis; 997 return rtFsFatObj_Close(&pThis->Core); 1099 int rc1 = rtFsFatFile_FlushMetaData(pThis); 1100 int rc2 = rtFsFatObj_Close(&pThis->Core); 1101 return RT_FAILURE(rc1) ? rc1 : rc2; 998 1102 } 999 1103 … … 1071 1175 1072 1176 /* 1073 * Do the reading cluster by cluster (converge clusters later).1177 * Do the reading cluster by cluster. 1074 1178 */ 1075 1179 int rc = VINF_SUCCESS; … … 1108 1212 break; 1109 1213 } 1214 1215 /* Update the offset and return. */ 1110 1216 pThis->offFile = off; 1111 1217 if (pcbRead) … … 1116 1222 1117 1223 /** 1224 * Changes the size of a file or directory FAT object. 1225 * 1226 * @returns IPRT status code 1227 * @param pObj The common object. 1228 * @param cbFile The new file size. 1229 */ 1230 static int rtFsFatObj_SetSize(PRTFSFATOBJ pObj, uint32_t cbFile) 1231 { 1232 AssertReturn((pObj->cbObject >> pObj->Clusters.cClusterByteShift) == pObj->Clusters.cClusters, VERR_INTERNAL_ERROR_3); 1233 1234 /* 1235 * Do nothing if the size didn't change. 1236 */ 1237 if (pObj->cbObject == cbFile) 1238 return VINF_SUCCESS; 1239 1240 /* 1241 * We need to at least update the directory entry, but quite likely allocate 1242 * or release clusters. 1243 */ 1244 int rc; 1245 uint32_t const cClustersNew = (cbFile + pObj->Clusters.cbCluster - 1) >> pObj->Clusters.cClusterByteShift; 1246 AssertReturn(pObj->pParentDir, VERR_INTERNAL_ERROR_2); 1247 if (pObj->Clusters.cClusters == cClustersNew) 1248 { 1249 pObj->cbObject = cbFile; 1250 rc = VINF_SUCCESS; 1251 } 1252 else if (pObj->cbObject < cbFile) 1253 { 1254 AssertFailed(); 1255 } 1256 else 1257 { 1258 /* Free clusters we don't need any more. */ 1259 rc = rtFsFatClusterMap_SetEndOfChain(pObj->pVol, rtFsFatChain_GetCluster(&pObj->Clusters, cClustersNew - 1)); 1260 if (RT_SUCCESS(rc)) 1261 { 1262 uint32_t iClusterToFree = cClustersNew; 1263 while (iClusterToFree < pObj->Clusters.cClusters && RT_SUCCESS(rc)) 1264 { 1265 rc = rtFsFatClusterMap_FreeCluster(pObj->pVol, rtFsFatChain_GetCluster(&pObj->Clusters, iClusterToFree)); 1266 iClusterToFree++; 1267 } 1268 1269 /* Update the chain structure. */ 1270 pObj->Clusters.cClusters = cClustersNew; 1271 } 1272 } 1273 if (RT_SUCCESS(rc)) 1274 { 1275 /* 1276 * Update the directory entry. 1277 */ 1278 uint32_t uWriteLock; 1279 PFATDIRENTRY pDirEntry; 1280 rc = rtFsFatDir_GetEntryForUpdate(pObj->pParentDir, pObj->offEntryInDir, &pDirEntry, &uWriteLock); 1281 if (RT_SUCCESS(rc)) 1282 { 1283 pDirEntry->cbFile = cbFile; 1284 rc = rtFsFatDir_PutEntryAfterUpdate(pObj->pParentDir, pDirEntry, uWriteLock); 1285 } 1286 } 1287 return rc; 1288 } 1289 1290 1291 /** 1118 1292 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite} 1119 1293 */ … … 1121 1295 { 1122 1296 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis; 1123 RT_NOREF(pThis, off, pSgBuf, fBlocking, pcbWritten); 1124 return VERR_NOT_IMPLEMENTED; 1125 } 1126 1127 1128 /** 1129 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush} 1130 */ 1131 static DECLCALLBACK(int) rtFsFatFile_Flush(void *pvThis) 1132 { 1133 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis; 1297 AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3); 1298 RT_NOREF(fBlocking); 1299 1300 if (off == -1) 1301 off = pThis->offFile; 1302 1303 /* 1304 * Do the reading cluster by cluster. 1305 */ 1306 int rc = VINF_SUCCESS; 1307 uint32_t cbWritten = 0; 1308 uint32_t cbLeft = pSgBuf->paSegs[0].cbSeg; 1309 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg; 1310 while (cbLeft > 0) 1311 { 1312 /* Figure out how much we can write. Checking for max file size and such. */ 1313 uint32_t cbToWrite = pThis->Core.Clusters.cbCluster - ((uint32_t)off & (pThis->Core.Clusters.cbCluster - 1)); 1314 if (cbToWrite > cbLeft) 1315 cbToWrite = cbLeft; 1316 uint64_t offNew = (uint64_t)off + cbToWrite; 1317 if (offNew < _4G) 1318 { /*likely*/ } 1319 else if ((uint64_t)off < _4G - 1U) 1320 cbToWrite = _4G - 1U - off; 1321 else 1322 { 1323 rc = VERR_FILE_TOO_BIG; 1324 break; 1325 } 1326 1327 /* Grow the file? */ 1328 if ((uint32_t)offNew > pThis->Core.cbObject) 1329 { 1330 rc = rtFsFatObj_SetSize(&pThis->Core, (uint32_t)offNew); 1331 if (RT_SUCCESS(rc)) 1332 { /* likely */} 1333 else 1334 break; 1335 } 1336 1337 /* Figure the disk offset. */ 1338 uint64_t offDisk = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, (uint32_t)off, pThis->Core.pVol); 1339 if (offDisk != UINT64_MAX) 1340 { 1341 rc = RTVfsFileWriteAt(pThis->Core.pVol->hVfsBacking, offDisk, pbSrc, cbToWrite, NULL); 1342 if (RT_SUCCESS(rc)) 1343 { 1344 off += cbToWrite; 1345 pbSrc += cbToWrite; 1346 cbWritten += cbToWrite; 1347 cbLeft -= cbToWrite; 1348 } 1349 else 1350 break; 1351 } 1352 else 1353 { 1354 rc = VERR_VFS_BOGUS_OFFSET; 1355 break; 1356 } 1357 } 1358 1359 /* Update the offset and return. */ 1360 pThis->offFile = off; 1361 if (pcbWritten) 1362 *pcbWritten = cbWritten; 1363 return VINF_SUCCESS; 1364 } 1365 1366 1367 /** 1368 * Flushes file meta data. 1369 * 1370 * @returns IPRT status code 1371 * @param pThis The file. 1372 */ 1373 static int rtFsFatFile_FlushMetaData(PRTFSFATFILE pThis) 1374 { 1134 1375 int rc = VINF_SUCCESS; 1135 1376 if (pThis->fMaybeDirtyFat) … … 1141 1382 rc = rc2; 1142 1383 } 1143 1384 return rc; 1385 } 1386 1387 /** 1388 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush} 1389 */ 1390 static DECLCALLBACK(int) rtFsFatFile_Flush(void *pvThis) 1391 { 1392 PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis; 1393 int rc1 = rtFsFatFile_FlushMetaData(pThis); 1144 1394 int rc2 = RTVfsFileFlush(pThis->Core.pVol->hVfsBacking); 1145 if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) 1146 rc = rc2; 1147 return rc; 1395 return RT_FAILURE(rc1) ? rc1 : rc2; 1148 1396 } 1149 1397 … … 1358 1606 */ 1359 1607 rtFsFatDir_AddOpenChild(pParentDir, &pNewFile->Core); 1360 return VINF_SUCCESS; 1608 1609 /* 1610 * Should we truncate the file or anything of that sort? 1611 */ 1612 if ( (fOpen & RTFILE_O_TRUNCATE) 1613 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE) 1614 rc = rtFsFatObj_SetSize(&pNewFile->Core, 0); 1615 if (RT_SUCCESS(rc)) 1616 return VINF_SUCCESS; 1361 1617 } 1362 1618 … … 1370 1626 1371 1627 1628 /** 1629 * Flush directory changes when having a fully buffered directory. 1630 * 1631 * @returns IPRT status code 1632 * @param pThis The directory. 1633 */ 1372 1634 static int rtFsFatDir_FlushFullyBuffered(PRTFSFATDIR pThis) 1373 1635 { 1374 1636 Assert(pThis->fFullyBuffered); 1375 return VINF_SUCCESS; 1376 } 1377 1378 1637 uint32_t const cbSector = pThis->Core.pVol->cbSector; 1638 RTVFSFILE const hVfsBacking = pThis->Core.pVol->hVfsBacking; 1639 int rc = VINF_SUCCESS; 1640 for (uint32_t i = 0; i < pThis->u.Full.cSectors; i++) 1641 if (pThis->u.Full.pbDirtySectors[i]) 1642 { 1643 int rc2 = RTVfsFileWriteAt(hVfsBacking, pThis->offEntriesOnDisk + i * cbSector, 1644 (uint8_t *)pThis->paEntries + i * cbSector, cbSector, NULL); 1645 if (RT_SUCCESS(rc2)) 1646 pThis->u.Full.pbDirtySectors[i] = false; 1647 else if (RT_SUCCESS(rc)) 1648 rc = rc2; 1649 } 1650 return rc; 1651 } 1652 1653 1654 /** 1655 * Flush directory changes when using simple buffering. 1656 * 1657 * @returns IPRT status code 1658 * @param pThis The directory. 1659 */ 1379 1660 static int rtFsFatDir_FlushSimple(PRTFSFATDIR pThis) 1380 1661 { … … 1396 1677 1397 1678 1679 /** 1680 * Flush directory changes. 1681 * 1682 * @returns IPRT status code 1683 * @param pThis The directory. 1684 */ 1398 1685 static int rtFsFatDir_Flush(PRTFSFATDIR pThis) 1399 1686 { … … 1404 1691 1405 1692 1406 static void rtFsFatDir_ReleaseBufferAfterReading(PRTFSFATDIR pThis, uint32_t uBufferReadLock)1407 {1408 RT_NOREF(pThis, uBufferReadLock);1409 }1410 1411 1412 1693 /** 1413 1694 * Gets one or more entires at @a offEntryInDir. 1695 * 1696 * Common worker for rtFsFatDir_GetEntriesAt and rtFsFatDir_GetEntryForUpdate 1414 1697 * 1415 1698 * @returns IPRT status code. 1416 1699 * @param pThis The directory. 1417 1700 * @param offEntryInDir The directory offset in bytes. 1701 * @param fForUpdate Whether it's for updating. 1418 1702 * @param ppaEntries Where to return pointer to the entry at 1419 1703 * @a offEntryInDir. … … 1424 1708 * done. 1425 1709 */ 1426 static int rtFsFatDir_GetEntriesAt (PRTFSFATDIR pThis, uint32_t offEntryInDir,1427 PCFATDIRENTRYUNION *ppaEntries, uint32_t *pcEntries, uint32_t *puBufferReadLock)1428 { 1429 *pu BufferReadLock = UINT32_MAX;1710 static int rtFsFatDir_GetEntriesAtCommon(PRTFSFATDIR pThis, uint32_t offEntryInDir, bool fForUpdate, 1711 PFATDIRENTRYUNION *ppaEntries, uint32_t *pcEntries, uint32_t *puLock) 1712 { 1713 *puLock = UINT32_MAX; 1430 1714 1431 1715 int rc; … … 1440 1724 * Fully buffered: Return pointer to all the entires starting at offEntryInDir. 1441 1725 */ 1442 *ppaEntries = &pThis->paEntries[idxEntryInDir];1443 *pcEntries = pThis->cEntries - idxEntryInDir;1444 *pu BufferReadLock = 1;1726 *ppaEntries = &pThis->paEntries[idxEntryInDir]; 1727 *pcEntries = pThis->cEntries - idxEntryInDir; 1728 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001); 1445 1729 rc = VINF_SUCCESS; 1446 1730 } … … 1454 1738 if (off < pVol->cbSector) 1455 1739 { 1456 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)];1457 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY);1458 *pu BufferReadLock = 1;1740 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)]; 1741 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY); 1742 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001); 1459 1743 rc = VINF_SUCCESS; 1460 1744 } … … 1480 1764 if (RT_SUCCESS(rc)) 1481 1765 { 1482 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)];1483 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY);1484 *pu BufferReadLock = 1;1766 *ppaEntries = &pThis->paEntries[off / sizeof(FATDIRENTRY)]; 1767 *pcEntries = (pVol->cbSector - off) / sizeof(FATDIRENTRY); 1768 *puLock = !fForUpdate ? 1 : UINT32_C(0x80000001); 1485 1769 rc = VINF_SUCCESS; 1486 1770 } … … 1497 1781 rc = VERR_FILE_NOT_FOUND; 1498 1782 return rc; 1783 } 1784 1785 1786 /** 1787 * Puts back a directory entry after updating it, releasing the write lock and 1788 * marking it dirty. 1789 * 1790 * @returns IPRT status code 1791 * @param pThis The directory. 1792 * @param pDirEntry The directory entry. 1793 * @param uWriteLock The write lock. 1794 */ 1795 static int rtFsFatDir_PutEntryAfterUpdate(PRTFSFATDIR pThis, PFATDIRENTRY pDirEntry, uint32_t uWriteLock) 1796 { 1797 Assert(uWriteLock == UINT32_C(0x80000001)); 1798 if (pThis->fFullyBuffered) 1799 { 1800 uint32_t idxSector = ((uintptr_t)pDirEntry - (uintptr_t)pThis->paEntries) / pThis->Core.pVol->cbSector; 1801 pThis->u.Full.pbDirtySectors[idxSector] = true; 1802 } 1803 else 1804 pThis->u.Simple.fDirty = true; 1805 return VINF_SUCCESS; 1806 } 1807 1808 1809 /** 1810 * Gets the pointer to the given directory entry for the purpose of updating it. 1811 * 1812 * Call rtFsFatDir_PutEntryAfterUpdate afterwards. 1813 * 1814 * @returns IPRT status code. 1815 * @param pThis The directory. 1816 * @param offEntryInDir The byte offset of the directory entry, within the 1817 * directory. 1818 * @param ppDirEntry Where to return the pointer to the directory entry. 1819 * @param puWriteLock Where to return the write lock. 1820 */ 1821 static int rtFsFatDir_GetEntryForUpdate(PRTFSFATDIR pThis, uint32_t offEntryInDir, PFATDIRENTRY *ppDirEntry, 1822 uint32_t *puWriteLock) 1823 { 1824 uint32_t cEntriesIgn; 1825 return rtFsFatDir_GetEntriesAtCommon(pThis, offEntryInDir, true /*fForUpdate*/, (PFATDIRENTRYUNION *)ppDirEntry, 1826 &cEntriesIgn, puWriteLock); 1827 } 1828 1829 1830 static void rtFsFatDir_ReleaseBufferAfterReading(PRTFSFATDIR pThis, uint32_t uBufferReadLock) 1831 { 1832 RT_NOREF(pThis, uBufferReadLock); 1833 Assert(uBufferReadLock == 1); 1834 } 1835 1836 1837 /** 1838 * Gets one or more entires at @a offEntryInDir. 1839 * 1840 * @returns IPRT status code. 1841 * @param pThis The directory. 1842 * @param offEntryInDir The directory offset in bytes. 1843 * @param ppaEntries Where to return pointer to the entry at 1844 * @a offEntryInDir. 1845 * @param pcEntries Where to return the number of entries 1846 * @a *ppaEntries points to. 1847 * @param puBufferReadLock Where to return the buffer read lock handle. 1848 * Call rtFsFatDir_ReleaseBufferAfterReading when 1849 * done. 1850 */ 1851 static int rtFsFatDir_GetEntriesAt(PRTFSFATDIR pThis, uint32_t offEntryInDir, 1852 PCFATDIRENTRYUNION *ppaEntries, uint32_t *pcEntries, uint32_t *puBufferReadLock) 1853 { 1854 return rtFsFatDir_GetEntriesAtCommon(pThis, offEntryInDir, false /*fForUpdate*/, (PFATDIRENTRYUNION *)ppaEntries, 1855 pcEntries, puBufferReadLock); 1499 1856 } 1500 1857
Note:
See TracChangeset
for help on using the changeset viewer.

