VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/isofs.cpp@ 33453

最後變更 在這個檔案從33453是 33453,由 vboxsync 提交於 14 年 前

IPRT/isofs.cpp: Documentation, cleaning up a bit.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 20.2 KB
 
1/* $Id: isofs.cpp 33453 2010-10-26 09:48:58Z vboxsync $ */
2/** @file
3 * IPRT - ISO 9660 file system handling.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/isofs.h>
32#include <iprt/path.h>
33
34
35/**
36 * Destroys the patch cache.
37 *
38 * @param pFile ISO handle.
39 */
40RTR3DECL(void) rtIsoFsDestroyPathCache(PRTISOFSFILE pFile)
41{
42 PRTISOFSPATHTABLEENTRY pNode = RTListNodeGetFirst(&pFile->listPaths, RTISOFSPATHTABLEENTRY, Node);
43 while (pNode)
44 {
45 PRTISOFSPATHTABLEENTRY pNext = RTListNodeGetNext(&pNode->Node, RTISOFSPATHTABLEENTRY, Node);
46 bool fLast = RTListNodeIsLast(&pFile->listPaths, &pNode->Node);
47
48 if (pNode->path)
49 RTStrFree(pNode->path);
50 if (pNode->path_full)
51 RTStrFree(pNode->path_full);
52 RTListNodeRemove(&pNode->Node);
53 RTMemFree(pNode);
54
55 if (fLast)
56 break;
57
58 pNode = pNext;
59 }
60}
61
62
63/**
64 * Adds a path entry to the path table list.
65 *
66 * @return IPRT status code.
67 * @param pList Path table list to add the path entry to.
68 * @param pszPath Path to add.
69 * @param pHeader Path header information to add.
70 */
71RTR3DECL(int) rtIsoFsAddToPathCache(PRTLISTNODE pList, const char *pszPath,
72 RTISOFSPATHTABLEHEADER *pHeader)
73{
74 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
75 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
76 AssertPtrReturn(pHeader, VERR_INVALID_PARAMETER);
77
78 PRTISOFSPATHTABLEENTRY pNode = (PRTISOFSPATHTABLEENTRY)RTMemAlloc(sizeof(RTISOFSPATHTABLEENTRY));
79 if (pNode == NULL)
80 return VERR_NO_MEMORY;
81
82 pNode->path = NULL;
83 if (RT_SUCCESS(RTStrAAppend(&pNode->path, pszPath)))
84 {
85 memcpy((RTISOFSPATHTABLEHEADER*)&pNode->header,
86 (RTISOFSPATHTABLEHEADER*)pHeader, sizeof(pNode->header));
87
88 pNode->path_full = NULL;
89 pNode->Node.pPrev = NULL;
90 pNode->Node.pNext = NULL;
91 RTListAppend(pList, &pNode->Node);
92 return VINF_SUCCESS;
93 }
94 return VERR_NO_MEMORY;
95}
96
97
98/**
99 * Retrieves the parent path of a given node, assuming that the path table
100 * (still) is in sync with the node's index.
101 *
102 * @return IPRT status code.
103 * @param pList Path table list to use.
104 * @param pNode Node of path table entry to lookup the full path for.
105 * @param pszPathNode Current (partial) parent path; needed for recursion.
106 * @param ppszPath Pointer to a pointer to store the retrieved full path to.
107 */
108RTR3DECL(int) rtIsoFsGetParentPathSub(PRTLISTNODE pList, PRTISOFSPATHTABLEENTRY pNode,
109 char *pszPathNode, char **ppszPath)
110{
111 int rc = VINF_SUCCESS;
112 /* Do we have a parent? */
113 if (pNode->header.parent_index > 1)
114 {
115 uint16_t idx = 1;
116 /* Get the parent of our current node (pNode) */
117 PRTISOFSPATHTABLEENTRY pNodeParent = RTListNodeGetFirst(pList, RTISOFSPATHTABLEENTRY, Node);
118 while (idx++ < pNode->header.parent_index)
119 pNodeParent = RTListNodeGetNext(&pNodeParent->Node, RTISOFSPATHTABLEENTRY, Node);
120 /* Construct intermediate path (parent + current path). */
121 char *pszPath;
122 if (RTStrAPrintf(&pszPath, "%s/%s", pNodeParent->path, pszPathNode))
123 {
124 /* ... and do the same with the parent's parent until we reached the root. */
125 rc = rtIsoFsGetParentPathSub(pList, pNodeParent, pszPath, ppszPath);
126 RTStrFree(pszPath);
127 }
128 else
129 rc = VERR_NO_MEMORY;
130 }
131 else /* No parent (left), this must be the root path then. */
132 {
133 char *pszPath = RTStrDup(pszPathNode);
134 *ppszPath = pszPath;
135 }
136 return rc;
137}
138
139
140/**
141 * Updates the path table cache of an ISO file.
142 *
143 * @return IPRT status code.
144 * @param pFile ISO handle.
145 */
146RTR3DECL(int) rtIsoFsUpdatePathCache(PRTISOFSFILE pFile)
147{
148 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
149 rtIsoFsDestroyPathCache(pFile);
150
151 RTListInit(&pFile->listPaths);
152
153 /* Seek to path tables. */
154 int rc = VINF_SUCCESS;
155 Assert(pFile->pvd.path_table_start_first > 16);
156 uint64_t uTableStart = (pFile->pvd.path_table_start_first * RTISOFS_SECTOR_SIZE);
157 Assert(uTableStart % RTISOFS_SECTOR_SIZE == 0); /* Make sure it's aligned. */
158 if (RTFileTell(pFile->file) != uTableStart)
159 rc = RTFileSeek(pFile->file, uTableStart, RTFILE_SEEK_BEGIN, &uTableStart);
160
161 /*
162 * Since this is a sequential format, for performance it's best to read the
163 * complete path table (every entry can have its own level (directory depth) first
164 * and the actual directories of the path table afterwards.
165 */
166
167 /* Read in the path table ... */
168 size_t cbLeft = pFile->pvd.path_table_size;
169 RTISOFSPATHTABLEHEADER header;
170 while ((cbLeft > 0) && RT_SUCCESS(rc))
171 {
172 size_t cbRead;
173 rc = RTFileRead(pFile->file, (RTISOFSPATHTABLEHEADER*)&header, sizeof(RTISOFSPATHTABLEHEADER), &cbRead);
174 if (RT_FAILURE(rc))
175 break;
176 cbLeft -= cbRead;
177 if (header.length)
178 {
179 Assert(cbLeft >= header.length);
180 Assert(header.length <= 31);
181 /* Allocate and read in the actual path name. */
182 char *pszName = RTStrAlloc(header.length + 1);
183 rc = RTFileRead(pFile->file, (char*)pszName, header.length, &cbRead);
184 if (RT_SUCCESS(rc))
185 {
186 cbLeft -= cbRead;
187 pszName[cbRead] = '\0'; /* Terminate string. */
188 /* Add entry to cache ... */
189 rc = rtIsoFsAddToPathCache(&pFile->listPaths, pszName, &header);
190 }
191 RTStrFree(pszName);
192 /* Read padding if required ... */
193 if ((header.length % 2) != 0) /* If we have an odd length, read/skip the padding byte. */
194 {
195 rc = RTFileSeek(pFile->file, 1, RTFILE_SEEK_CURRENT, NULL);
196 cbLeft--;
197 }
198 }
199 }
200
201 /* Transform path names into full paths. This is a bit ugly right now. */
202 PRTISOFSPATHTABLEENTRY pNode = RTListNodeGetLast(&pFile->listPaths, RTISOFSPATHTABLEENTRY, Node);
203 while ( pNode
204 && !RTListNodeIsFirst(&pFile->listPaths, &pNode->Node)
205 && RT_SUCCESS(rc))
206 {
207 rc = rtIsoFsGetParentPathSub(&pFile->listPaths, pNode,
208 pNode->path, &pNode->path_full);
209 if (RT_SUCCESS(rc))
210 pNode = RTListNodeGetPrev(&pNode->Node, RTISOFSPATHTABLEENTRY, Node);
211 }
212
213 return rc;
214}
215
216
217RTR3DECL(int) RTIsoFsOpen(PRTISOFSFILE pFile, const char *pszFileName)
218{
219 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
220 AssertPtrReturn(pszFileName, VERR_INVALID_PARAMETER);
221
222 RTListInit(&pFile->listPaths);
223#if 0
224 Assert(sizeof(RTISOFSDATESHORT) == 7);
225 Assert(sizeof(RTISOFSDATELONG) == 17);
226 int l = sizeof(RTISOFSDIRRECORD);
227 RTPrintf("RTISOFSDIRRECORD=%ld\n", l);
228 Assert(l == 33);
229 /* Each volume descriptor exactly occupies one sector. */
230 l = sizeof(RTISOFSPRIVOLDESC);
231 RTPrintf("RTISOFSPRIVOLDESC=%ld\n", l);
232 Assert(l == RTISOFS_SECTOR_SIZE);
233#endif
234 int rc = RTFileOpen(&pFile->file, pszFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
235 if (RT_SUCCESS(rc))
236 {
237 uint64_t cbSize;
238 rc = RTFileGetSize(pFile->file, &cbSize);
239 if ( RT_SUCCESS(rc)
240 && cbSize > 16 * RTISOFS_SECTOR_SIZE)
241 {
242 uint64_t cbOffset = 16 * RTISOFS_SECTOR_SIZE; /* Start reading at 32k. */
243 size_t cbRead;
244 RTISOFSPRIVOLDESC pvd;
245 bool fFoundPrimary = false;
246 bool fIsValid = false;
247 while (cbOffset < _1M)
248 {
249 /* Get primary descriptor. */
250 rc = RTFileRead(pFile->file, (PRTISOFSPRIVOLDESC)&pvd, sizeof(RTISOFSPRIVOLDESC), &cbRead);
251 if (RT_FAILURE(rc) || cbRead < sizeof(RTISOFSPRIVOLDESC))
252 break;
253 if ( RTStrStr((char*)pvd.name_id, RTISOFS_STANDARD_ID)
254 && pvd.type == 0x1 /* Primary Volume Descriptor */)
255 {
256 memcpy((PRTISOFSPRIVOLDESC)&pFile->pvd,
257 (PRTISOFSPRIVOLDESC)&pvd, sizeof(RTISOFSPRIVOLDESC));
258 fFoundPrimary = true;
259 }
260 else if(pvd.type == 0xff /* Termination Volume Descriptor */)
261 {
262 if (fFoundPrimary)
263 fIsValid = true;
264 break;
265 }
266 cbOffset += sizeof(RTISOFSPRIVOLDESC);
267 }
268
269 if (fIsValid)
270 rc = rtIsoFsUpdatePathCache(pFile);
271 else
272 rc = VERR_INVALID_PARAMETER;
273 }
274 if (RT_FAILURE(rc))
275 RTIsoFsClose(pFile);
276 }
277 return rc;
278}
279
280
281RTR3DECL(void) RTIsoFsClose(PRTISOFSFILE pFile)
282{
283 if (pFile)
284 {
285 rtIsoFsDestroyPathCache(pFile);
286 RTFileClose(pFile->file);
287 }
288}
289
290
291/**
292 * Parses an extent given at the specified sector + size and
293 * searches for a file name to return an allocated directory record.
294 *
295 * @return IPRT status code.
296 * @param pFile ISO handle.
297 * @param pszFileName Absolute file name to search for.
298 * @param uExtentSector Sector of extent.
299 * @param cbExtent Size (in bytes) of extent.
300 * @param ppRec Pointer to a pointer to return the
301 * directory record. Must be free'd with
302 * rtIsoFsFreeDirectoryRecord().
303 */
304RTR3DECL(int) rtIsoFsFindEntry(PRTISOFSFILE pFile, const char *pszFileName,
305 uint32_t uExtentSector, uint32_t cbExtent /* Bytes */,
306 PRTISOFSDIRRECORD *ppRec)
307{
308 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
309 Assert(uExtentSector > 16);
310
311 int rc = RTFileSeek(pFile->file, uExtentSector * RTISOFS_SECTOR_SIZE,
312 RTFILE_SEEK_BEGIN, NULL);
313 if (RT_SUCCESS(rc))
314 {
315 rc = VERR_FILE_NOT_FOUND;
316
317 uint8_t uBuffer[RTISOFS_SECTOR_SIZE];
318 size_t cbLeft = cbExtent;
319 while (!RT_SUCCESS(rc) && cbLeft > 0)
320 {
321 size_t cbRead;
322 int rc2 = RTFileRead(pFile->file, (void*)&uBuffer, sizeof(uBuffer), &cbRead);
323 Assert(RT_SUCCESS(rc2) && cbRead == RTISOFS_SECTOR_SIZE);
324 cbLeft -= cbRead;
325
326 uint32_t idx = 0;
327 while (idx < cbRead)
328 {
329 PRTISOFSDIRRECORD pCurRecord = (PRTISOFSDIRRECORD)&uBuffer[idx];
330 if (pCurRecord->record_length == 0)
331 break;
332
333 Assert(pCurRecord->name_len > 0);
334 char *pszName = RTStrAlloc(pCurRecord->name_len + 1);
335 AssertPtr(pszName);
336 Assert(idx + sizeof(RTISOFSDIRRECORD) < cbRead);
337 memcpy(pszName, &uBuffer[idx + sizeof(RTISOFSDIRRECORD)], pCurRecord->name_len);
338
339 if ( pCurRecord->name_len == 1
340 && pszName[0] == 0x0)
341 {
342 /* This is a "." directory (self). */
343 }
344 else if ( pCurRecord->name_len == 1
345 && pszName[0] == 0x1)
346 {
347 /* This is a ".." directory (parent). */
348 }
349 else /* Regular directory or file */
350 {
351 if (pCurRecord->flags & RT_BIT(1)) /* Directory */
352 {
353 /* We don't recursively go into directories
354 * because we already have the cached path table. */
355 pszName[pCurRecord->name_len] = 0;
356 /*rc = rtIsoFsParseDir(pFile, pszFileName,
357 pDirHdr->extent_location, pDirHdr->extent_data_length);*/
358 }
359 else /* File */
360 {
361 /* Get last occurence of ";" and cut it off. */
362 char *pTerm = strrchr(pszName, ';');
363 if (pTerm)
364 pszName[pTerm - pszName] = 0;
365
366 /* Don't use case sensitive comparison here, in IS0 9660 all
367 * file / directory names are UPPERCASE. */
368 if (!RTStrICmp(pszName, pszFileName))
369 {
370 PRTISOFSDIRRECORD pRec = (PRTISOFSDIRRECORD)RTMemAlloc(sizeof(RTISOFSDIRRECORD));
371 if (pRec)
372 {
373 memcpy(pRec, pCurRecord, sizeof(RTISOFSDIRRECORD));
374 *ppRec = pRec;
375 rc = VINF_SUCCESS;
376 break;
377 }
378 else
379 rc = VERR_NO_MEMORY;
380 }
381 }
382 }
383 idx += pCurRecord->record_length;
384 }
385 }
386 }
387 return rc;
388}
389
390
391/**
392 * Retrieves the sector of a file extent given by the
393 * full file path within the ISO.
394 *
395 * @return IPRT status code.
396 * @param pFile ISO handle.
397 * @param pszPath File path to resolve.
398 * @param puSector Pointer where to store the found sector to.
399 */
400RTR3DECL(int) rtIsoFsResolvePath(PRTISOFSFILE pFile, const char *pszPath, uint32_t *puSector)
401{
402 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
403 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
404 AssertPtrReturn(puSector, VERR_INVALID_PARAMETER);
405
406 int rc = VERR_FILE_NOT_FOUND;
407 char *pszTemp = RTStrDup(pszPath);
408 if (pszTemp)
409 {
410 RTPathStripFilename(pszTemp);
411
412 bool bFound = false;
413 PRTISOFSPATHTABLEENTRY pNode;
414 if (!RTStrCmp(pszTemp, ".")) /* Root directory? Use first node! */
415 {
416 pNode = RTListNodeGetFirst(&pFile->listPaths, RTISOFSPATHTABLEENTRY, Node);
417 bFound = true;
418 }
419 else
420 {
421 RTListForEach(&pFile->listPaths, pNode, RTISOFSPATHTABLEENTRY, Node)
422 {
423 if ( pNode->path_full != NULL /* Root does not have a path! */
424 && !RTStrICmp(pNode->path_full, pszTemp))
425 {
426 bFound = true;
427 break;
428 }
429 }
430 }
431 if (bFound)
432 {
433 *puSector = pNode->header.sector_dir_table;
434 rc = VINF_SUCCESS;
435 }
436 RTStrFree(pszTemp);
437 }
438 else
439 rc = VERR_NO_MEMORY;
440 return rc;
441}
442
443
444/**
445 * Allocates a new directory record.
446 *
447 * @return Pointer to the newly allocated directory record.
448 */
449RTR3DECL(PRTISOFSDIRRECORD) rtIsoFsCreateDirectoryRecord(void)
450{
451 PRTISOFSDIRRECORD pRecord = (PRTISOFSDIRRECORD)RTMemAlloc(sizeof(RTISOFSDIRRECORD));
452 return pRecord;
453}
454
455
456/**
457 * Frees a previously allocated directory record.
458 *
459 * @return IPRT status code.
460 */
461RTR3DECL(void) rtIsoFsFreeDirectoryRecord(PRTISOFSDIRRECORD pRecord)
462{
463 RTMemFree(pRecord);
464}
465
466
467/**
468 * Returns an allocated directory record for a given file.
469 *
470 * @return IPRT status code.
471 * @param pFile ISO handle.
472 * @param pszPath File path to resolve.
473 * @param ppRecord Pointer to a pointer to return the
474 * directory record. Must be free'd with
475 * rtIsoFsFreeDirectoryRecord().
476 */
477RTR3DECL(int) rtIsoFsGetDirectoryRecord(PRTISOFSFILE pFile, const char *pszPath,
478 PRTISOFSDIRRECORD *ppRecord)
479{
480 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
481 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
482 AssertPtrReturn(ppRecord, VERR_INVALID_PARAMETER);
483
484 uint32_t uSector;
485 int rc = rtIsoFsResolvePath(pFile, pszPath, &uSector);
486 if (RT_SUCCESS(rc))
487 {
488 /* Seek and read the directory record of given file. */
489 rc = RTFileSeek(pFile->file, uSector * RTISOFS_SECTOR_SIZE,
490 RTFILE_SEEK_BEGIN, NULL);
491 if (RT_SUCCESS(rc))
492 {
493 size_t cbRead;
494 PRTISOFSDIRRECORD pRecord = rtIsoFsCreateDirectoryRecord();
495 if (pRecord)
496 {
497 rc = RTFileRead(pFile->file, (PRTISOFSDIRRECORD)pRecord, sizeof(RTISOFSDIRRECORD), &cbRead);
498 if (RT_SUCCESS(rc))
499 {
500 Assert(cbRead == sizeof(RTISOFSDIRRECORD));
501 *ppRecord = pRecord;
502 }
503 if (RT_FAILURE(rc))
504 rtIsoFsFreeDirectoryRecord(pRecord);
505 }
506 else
507 rc = VERR_NO_MEMORY;
508 }
509 }
510 return rc;
511}
512
513
514RTR3DECL(int) RTIsoFsGetFileInfo(PRTISOFSFILE pFile, const char *pszPath,
515 uint32_t *pcbOffset, size_t *pcbLength)
516{
517 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
518 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
519 AssertPtrReturn(pcbOffset, VERR_INVALID_PARAMETER);
520
521 PRTISOFSDIRRECORD pDirRecord;
522 int rc = rtIsoFsGetDirectoryRecord(pFile, pszPath, &pDirRecord);
523 if (RT_SUCCESS(rc))
524 {
525 /* Get actual file record. */
526 PRTISOFSDIRRECORD pFileRecord;
527 rc = rtIsoFsFindEntry(pFile,
528 RTPathFilename(pszPath),
529 pDirRecord->extent_location,
530 pDirRecord->extent_data_length,
531 &pFileRecord);
532 if (RT_SUCCESS(rc))
533 {
534 *pcbOffset = pFileRecord->extent_location * RTISOFS_SECTOR_SIZE;
535 *pcbLength = pFileRecord->extent_data_length;
536 rtIsoFsFreeDirectoryRecord(pFileRecord);
537 }
538 rtIsoFsFreeDirectoryRecord(pDirRecord);
539 }
540 return rc;
541}
542
543
544RTR3DECL(int) RTIsoFsExtractFile(PRTISOFSFILE pFile, const char *pszSource,
545 const char *pszDest)
546{
547 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
548 AssertPtrReturn(pszSource, VERR_INVALID_PARAMETER);
549 AssertPtrReturn(pszDest, VERR_INVALID_PARAMETER);
550
551 uint32_t cbOffset;
552 size_t cbLength;
553 int rc = RTIsoFsGetFileInfo(pFile, pszSource, &cbOffset, &cbLength);
554 if (RT_SUCCESS(rc))
555 {
556 rc = RTFileSeek(pFile->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
557 if (RT_SUCCESS(rc))
558 {
559 RTFILE fileDest;
560 rc = RTFileOpen(&fileDest, pszDest, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
561 if (RT_SUCCESS(rc))
562 {
563 size_t cbToRead, cbRead, cbWritten;
564 uint8_t byBuffer[_64K];
565 while ( cbLength > 0
566 && RT_SUCCESS(rc))
567 {
568 cbToRead = RT_MIN(cbLength, _64K);
569 rc = RTFileRead(pFile->file, (uint8_t*)byBuffer, cbToRead, &cbRead);
570 if (RT_FAILURE(rc))
571 break;
572 rc = RTFileWrite(fileDest, (uint8_t*)byBuffer, cbRead, &cbWritten);
573 if (RT_FAILURE(rc))
574 break;
575 cbLength -= cbRead;
576 }
577 RTFileClose(fileDest);
578 }
579 }
580 }
581 return rc;
582}
583
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette