VirtualBox

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

Last change on this file since 33223 was 33148, checked in by vboxsync, 14 years ago

IPRT/IsoFS: Raise block size to 64K.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: isofs.cpp 33148 2010-10-15 11:06:49Z 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.virtualbox.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
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 *
65 *
66 * @return int
67 *
68 * @param pList
69 * @param pszPath
70 * @param pHeader
71 */
72RTR3DECL(int) rtIsoFsAddToPathCache(PRTLISTNODE pList, const char *pszPath,
73 RTISOFSPATHTABLEHEADER *pHeader)
74{
75 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
76 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
77 AssertPtrReturn(pHeader, VERR_INVALID_PARAMETER);
78
79 PRTISOFSPATHTABLEENTRY pNode = (PRTISOFSPATHTABLEENTRY)RTMemAlloc(sizeof(RTISOFSPATHTABLEENTRY));
80 if (pNode == NULL)
81 return VERR_NO_MEMORY;
82
83 pNode->path = NULL;
84 if (RT_SUCCESS(RTStrAAppend(&pNode->path, pszPath)))
85 {
86 memcpy((RTISOFSPATHTABLEHEADER*)&pNode->header,
87 (RTISOFSPATHTABLEHEADER*)pHeader, sizeof(pNode->header));
88
89 pNode->path_full = NULL;
90 pNode->Node.pPrev = NULL;
91 pNode->Node.pNext = NULL;
92 RTListAppend(pList, &pNode->Node);
93 return VINF_SUCCESS;
94 }
95 return VERR_NO_MEMORY;
96}
97
98
99/**
100 *
101 *
102 * @return int
103 *
104 * @param pList
105 * @param pNode
106 * @param pszPathNode
107 * @param ppszPath
108 */
109RTR3DECL(int) rtIsoFsGetParentPathSub(PRTLISTNODE pList, PRTISOFSPATHTABLEENTRY pNode,
110 char *pszPathNode, char **ppszPath)
111{
112 int rc = VINF_SUCCESS;
113 if (pNode->header.parent_index > 1)
114 {
115 uint16_t idx = 1;
116 PRTISOFSPATHTABLEENTRY pNodeParent = RTListNodeGetFirst(pList, RTISOFSPATHTABLEENTRY, Node);
117 while (idx++ < pNode->header.parent_index)
118 pNodeParent = RTListNodeGetNext(&pNodeParent->Node, RTISOFSPATHTABLEENTRY, Node);
119 char *pszPath;
120 if (RTStrAPrintf(&pszPath, "%s/%s", pNodeParent->path, pszPathNode))
121 {
122 rc = rtIsoFsGetParentPathSub(pList, pNodeParent, pszPath, ppszPath);
123 RTStrFree(pszPath);
124 }
125 else
126 rc = VERR_NO_MEMORY;
127 }
128 else
129 {
130 char *pszPath = RTStrDup(pszPathNode);
131 *ppszPath = pszPath;
132 }
133 return rc;
134}
135
136
137/**
138 *
139 *
140 * @return int
141 *
142 * @param pFile
143 */
144RTR3DECL(int) rtIsoFsUpdatePathCache(PRTISOFSFILE pFile)
145{
146 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
147 rtIsoFsDestroyPathCache(pFile);
148
149 RTListInit(&pFile->listPaths);
150
151 /* Seek to path tables. */
152 int rc = VINF_SUCCESS;
153 Assert(pFile->pvd.path_table_start_first > 16);
154 uint64_t uTableStart = (pFile->pvd.path_table_start_first * RTISOFS_SECTOR_SIZE);
155 Assert(uTableStart % RTISOFS_SECTOR_SIZE == 0); /* Make sure it's aligned. */
156 if (RTFileTell(pFile->file) != uTableStart)
157 rc = RTFileSeek(pFile->file, uTableStart, RTFILE_SEEK_BEGIN, &uTableStart);
158
159 /*
160 * Since this is a sequential format, for performance it's best to read the
161 * complete path table (every entry can have its own level (directory depth) first
162 * and the actual directories of the path table afterwards.
163 */
164
165 /* Read in the path table ... */
166 size_t cbLeft = pFile->pvd.path_table_size;
167 RTISOFSPATHTABLEHEADER header;
168 while ((cbLeft > 0) && RT_SUCCESS(rc))
169 {
170 size_t cbRead;
171 rc = RTFileRead(pFile->file, (RTISOFSPATHTABLEHEADER*)&header, sizeof(RTISOFSPATHTABLEHEADER), &cbRead);
172 if (RT_FAILURE(rc))
173 break;
174 cbLeft -= cbRead;
175 if (header.length)
176 {
177 Assert(cbLeft >= header.length);
178 Assert(header.length <= 31);
179 /* Allocate and read in the actual path name. */
180 char *pszName = RTStrAlloc(header.length + 1);
181 rc = RTFileRead(pFile->file, (char*)pszName, header.length, &cbRead);
182 if (RT_SUCCESS(rc))
183 {
184 cbLeft -= cbRead;
185 pszName[cbRead] = '\0'; /* Terminate string. */
186 /* Add entry to cache ... */
187 rc = rtIsoFsAddToPathCache(&pFile->listPaths, pszName, &header);
188 }
189 RTStrFree(pszName);
190 /* Read padding if required ... */
191 if ((header.length % 2) != 0) /* If we have an odd length, read/skip the padding byte. */
192 {
193 rc = RTFileSeek(pFile->file, 1, RTFILE_SEEK_CURRENT, NULL);
194 cbLeft--;
195 }
196 }
197 }
198
199 /* Transform path names into full paths. This is a bit ugly right now. */
200 PRTISOFSPATHTABLEENTRY pNode = RTListNodeGetLast(&pFile->listPaths, RTISOFSPATHTABLEENTRY, Node);
201 while ( pNode
202 && !RTListNodeIsFirst(&pFile->listPaths, &pNode->Node)
203 && RT_SUCCESS(rc))
204 {
205 rc = rtIsoFsGetParentPathSub(&pFile->listPaths, pNode,
206 pNode->path, &pNode->path_full);
207 if (RT_SUCCESS(rc))
208 pNode = RTListNodeGetPrev(&pNode->Node, RTISOFSPATHTABLEENTRY, Node);
209 }
210
211 return rc;
212}
213
214
215/**
216 *
217 *
218 * @return int
219 *
220 * @param pszFileName
221 * @param pFile
222 */
223RTR3DECL(int) RTIsoFsOpen(PRTISOFSFILE pFile, const char *pszFileName)
224{
225 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
226 AssertPtrReturn(pszFileName, VERR_INVALID_PARAMETER);
227
228 RTListInit(&pFile->listPaths);
229#if 0
230 Assert(sizeof(RTISOFSDATESHORT) == 7);
231 Assert(sizeof(RTISOFSDATELONG) == 17);
232 int l = sizeof(RTISOFSDIRRECORD);
233 RTPrintf("RTISOFSDIRRECORD=%ld\n", l);
234 Assert(l == 33);
235 /* Each volume descriptor exactly occupies one sector. */
236 l = sizeof(RTISOFSPRIVOLDESC);
237 RTPrintf("RTISOFSPRIVOLDESC=%ld\n", l);
238 Assert(l == RTISOFS_SECTOR_SIZE);
239#endif
240 int rc = RTFileOpen(&pFile->file, pszFileName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
241 if (RT_SUCCESS(rc))
242 {
243 uint64_t cbSize;
244 rc = RTFileGetSize(pFile->file, &cbSize);
245 if ( RT_SUCCESS(rc)
246 && cbSize > 16 * RTISOFS_SECTOR_SIZE)
247 {
248 uint64_t cbOffset = 16 * RTISOFS_SECTOR_SIZE; /* Start reading at 32k. */
249 size_t cbRead;
250 RTISOFSPRIVOLDESC pvd;
251 bool fFoundPrimary = false;
252 bool fIsValid = false;
253 while (cbOffset < _1M)
254 {
255 /* Get primary descriptor. */
256 rc = RTFileRead(pFile->file, (PRTISOFSPRIVOLDESC)&pvd, sizeof(RTISOFSPRIVOLDESC), &cbRead);
257 if (RT_FAILURE(rc) || cbRead < sizeof(RTISOFSPRIVOLDESC))
258 break;
259 if ( RTStrStr((char*)pvd.name_id, RTISOFS_STANDARD_ID)
260 && pvd.type == 0x1 /* Primary Volume Descriptor */)
261 {
262 memcpy((PRTISOFSPRIVOLDESC)&pFile->pvd,
263 (PRTISOFSPRIVOLDESC)&pvd, sizeof(RTISOFSPRIVOLDESC));
264 fFoundPrimary = true;
265 }
266 else if(pvd.type == 0xff /* Termination Volume Descriptor */)
267 {
268 if (fFoundPrimary)
269 fIsValid = true;
270 break;
271 }
272 cbOffset += sizeof(RTISOFSPRIVOLDESC);
273 }
274
275 if (fIsValid)
276 rc = rtIsoFsUpdatePathCache(pFile);
277 else
278 rc = VERR_INVALID_PARAMETER;
279 }
280 if (RT_FAILURE(rc))
281 RTIsoFsClose(pFile);
282 }
283 return rc;
284}
285
286
287/**
288 *
289 *
290 *
291 * @param pFile
292 */
293RTR3DECL(void) RTIsoFsClose(PRTISOFSFILE pFile)
294{
295 if (pFile)
296 {
297 rtIsoFsDestroyPathCache(pFile);
298 RTFileClose(pFile->file);
299 }
300}
301
302
303/**
304 * Parses an extent given at the specified sector + size and
305 * returns a directory record.
306 *
307 * @return int
308 *
309 * @param pFile
310 * @param pszFileName
311 * @param uExtentSector
312 * @param cbExtent
313 * @param ppRec
314 */
315RTR3DECL(int) rtIsoFsFindEntry(PRTISOFSFILE pFile, const char *pszFileName,
316 uint32_t uExtentSector, uint32_t cbExtent /* Bytes */,
317 PRTISOFSDIRRECORD *ppRec)
318{
319 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
320 Assert(uExtentSector > 16);
321
322 int rc = RTFileSeek(pFile->file, uExtentSector * RTISOFS_SECTOR_SIZE,
323 RTFILE_SEEK_BEGIN, NULL);
324 if (RT_SUCCESS(rc))
325 {
326 rc = VERR_FILE_NOT_FOUND;
327
328 uint8_t uBuffer[RTISOFS_SECTOR_SIZE];
329 size_t cbLeft = cbExtent;
330 while (!RT_SUCCESS(rc) && cbLeft > 0)
331 {
332 size_t cbRead;
333 int rc2 = RTFileRead(pFile->file, (void*)&uBuffer, sizeof(uBuffer), &cbRead);
334 Assert(RT_SUCCESS(rc2) && cbRead == RTISOFS_SECTOR_SIZE);
335 cbLeft -= cbRead;
336
337 uint32_t idx = 0;
338 while (idx < cbRead)
339 {
340 PRTISOFSDIRRECORD pCurRecord = (PRTISOFSDIRRECORD)&uBuffer[idx];
341 if (pCurRecord->record_length == 0)
342 break;
343
344 Assert(pCurRecord->name_len > 0);
345 char *pszName = RTStrAlloc(pCurRecord->name_len + 1);
346 AssertPtr(pszName);
347 Assert(idx + sizeof(RTISOFSDIRRECORD) < cbRead);
348 memcpy(pszName, &uBuffer[idx + sizeof(RTISOFSDIRRECORD)], pCurRecord->name_len);
349
350 if ( pCurRecord->name_len == 1
351 && pszName[0] == 0x0)
352 {
353 /* This is a "." directory (self). */
354 }
355 else if ( pCurRecord->name_len == 1
356 && pszName[0] == 0x1)
357 {
358 /* This is a ".." directory (parent). */
359 }
360 else /* Regular directory or file */
361 {
362 if (pCurRecord->flags & RT_BIT(1)) /* Directory */
363 {
364 /* We don't recursively go into directories
365 * because we already have the cached path table. */
366 pszName[pCurRecord->name_len] = 0;
367 /*rc = rtIsoFsParseDir(pFile, pszFileName,
368 pDirHdr->extent_location, pDirHdr->extent_data_length);*/
369 }
370 else /* File */
371 {
372 /* Get last occurence of ";" and cut it off. */
373 char *pTerm = strrchr(pszName, ';');
374 if (pTerm)
375 pszName[pTerm - pszName] = 0;
376
377 /* Don't use case sensitive comparison here, in IS0 9660 all
378 * file / directory names are UPPERCASE. */
379 if (!RTStrICmp(pszName, pszFileName))
380 {
381 PRTISOFSDIRRECORD pRec = (PRTISOFSDIRRECORD)RTMemAlloc(sizeof(RTISOFSDIRRECORD));
382 if (pRec)
383 {
384 memcpy(pRec, pCurRecord, sizeof(RTISOFSDIRRECORD));
385 *ppRec = pRec;
386 rc = VINF_SUCCESS;
387 break;
388 }
389 else
390 rc = VERR_NO_MEMORY;
391 }
392 }
393 }
394 idx += pCurRecord->record_length;
395 }
396 }
397 }
398 return rc;
399}
400
401
402/**
403 *
404 *
405 * @return int
406 *
407 * @param pFile
408 * @param pszPath
409 * @param puSector
410 */
411RTR3DECL(int) rtIsoFsResolvePath(PRTISOFSFILE pFile, const char *pszPath, uint32_t *puSector)
412{
413 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
414 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
415 AssertPtrReturn(puSector, VERR_INVALID_PARAMETER);
416
417 int rc = VERR_FILE_NOT_FOUND;
418 char *pszTemp = RTStrDup(pszPath);
419 if (pszTemp)
420 {
421 RTPathStripFilename(pszTemp);
422
423 bool bFound = false;
424 PRTISOFSPATHTABLEENTRY pNode;
425 if (!RTStrCmp(pszTemp, ".")) /* Root directory? Use first node! */
426 {
427 pNode = RTListNodeGetFirst(&pFile->listPaths, RTISOFSPATHTABLEENTRY, Node);
428 bFound = true;
429 }
430 else
431 {
432 RTListForEach(&pFile->listPaths, pNode, RTISOFSPATHTABLEENTRY, Node)
433 {
434 if ( pNode->path_full != NULL /* Root does not have a path! */
435 && !RTStrICmp(pNode->path_full, pszTemp))
436 {
437 bFound = true;
438 break;
439 }
440 }
441 }
442 if (bFound)
443 {
444 *puSector = pNode->header.sector_dir_table;
445 rc = VINF_SUCCESS;
446 }
447 RTStrFree(pszTemp);
448 }
449 else
450 rc = VERR_NO_MEMORY;
451 return rc;
452}
453
454
455/**
456 *
457 *
458 * @return int
459 *
460 * @param pFile
461 * @param pszPath
462 * @param ppRecord
463 */
464RTR3DECL(int) rtIsoFsGetDirectoryRecord(PRTISOFSFILE pFile, const char *pszPath,
465 PRTISOFSDIRRECORD *ppRecord)
466{
467 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
468 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
469 AssertPtrReturn(ppRecord, VERR_INVALID_PARAMETER);
470
471 uint32_t uSector;
472 int rc = rtIsoFsResolvePath(pFile, pszPath, &uSector);
473 if (RT_SUCCESS(rc))
474 {
475 /* Seek and read the directory record of given file. */
476 rc = RTFileSeek(pFile->file, uSector * RTISOFS_SECTOR_SIZE,
477 RTFILE_SEEK_BEGIN, NULL);
478 if (RT_SUCCESS(rc))
479 {
480 size_t cbRead;
481 PRTISOFSDIRRECORD pRecord = (PRTISOFSDIRRECORD)RTMemAlloc(sizeof(RTISOFSDIRRECORD));
482 if (pRecord)
483 {
484 rc = RTFileRead(pFile->file, (PRTISOFSDIRRECORD)pRecord, sizeof(RTISOFSDIRRECORD), &cbRead);
485 if (RT_SUCCESS(rc))
486 {
487 Assert(cbRead == sizeof(RTISOFSDIRRECORD));
488 *ppRecord = pRecord;
489 }
490 if (RT_FAILURE(rc))
491 RTMemFree(pRecord);
492 }
493 else
494 rc = VERR_NO_MEMORY;
495 }
496 }
497 return rc;
498}
499
500
501RTR3DECL(int) RTIsoFsGetFileInfo(PRTISOFSFILE pFile, const char *pszPath,
502 uint32_t *pcbOffset, size_t *pcbLength)
503{
504 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
505 AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
506 AssertPtrReturn(pcbOffset, VERR_INVALID_PARAMETER);
507
508 PRTISOFSDIRRECORD pDirRecord;
509 int rc = rtIsoFsGetDirectoryRecord(pFile, pszPath, &pDirRecord);
510 if (RT_SUCCESS(rc))
511 {
512 /* Get actual file record. */
513 PRTISOFSDIRRECORD pFileRecord;
514 rc = rtIsoFsFindEntry(pFile,
515 RTPathFilename(pszPath),
516 pDirRecord->extent_location,
517 pDirRecord->extent_data_length,
518 &pFileRecord);
519 if (RT_SUCCESS(rc))
520 {
521 *pcbOffset = pFileRecord->extent_location * RTISOFS_SECTOR_SIZE;
522 *pcbLength = pFileRecord->extent_data_length;
523 RTMemFree(pFileRecord);
524 }
525 RTMemFree(pDirRecord);
526 }
527 return rc;
528}
529
530
531RTR3DECL(int) RTIsoFsExtractFile(PRTISOFSFILE pFile, const char *pszSource,
532 const char *pszDest)
533{
534 AssertPtrReturn(pFile, VERR_INVALID_PARAMETER);
535 AssertPtrReturn(pszSource, VERR_INVALID_PARAMETER);
536 AssertPtrReturn(pszDest, VERR_INVALID_PARAMETER);
537
538 uint32_t cbOffset;
539 size_t cbLength;
540 int rc = RTIsoFsGetFileInfo(pFile, pszSource, &cbOffset, &cbLength);
541 if (RT_SUCCESS(rc))
542 {
543 rc = RTFileSeek(pFile->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
544 if (RT_SUCCESS(rc))
545 {
546 RTFILE fileDest;
547 rc = RTFileOpen(&fileDest, pszDest, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
548 if (RT_SUCCESS(rc))
549 {
550 size_t cbToRead, cbRead, cbWritten;
551 uint8_t byBuffer[_64K];
552 while ( cbLength > 0
553 && RT_SUCCESS(rc))
554 {
555 cbToRead = RT_MIN(cbLength, _64K);
556 rc = RTFileRead(pFile->file, (uint8_t*)byBuffer, cbToRead, &cbRead);
557 if (RT_FAILURE(rc))
558 break;
559 rc = RTFileWrite(fileDest, (uint8_t*)byBuffer, cbRead, &cbWritten);
560 if (RT_FAILURE(rc))
561 break;
562 cbLength -= cbRead;
563 }
564 RTFileClose(fileDest);
565 }
566 }
567 }
568 return rc;
569}
570
Note: See TracBrowser for help on using the repository browser.

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