]>
Commit | Line | Data |
---|---|---|
75b89a82 | 1 | /* |
57c72a9a | 2 | * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. |
75b89a82 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
4f6e3300 | 6 | * The contents of this file constitute Original Code as defined in and |
57c72a9a | 7 | * are subject to the Apple Public Source License Version 2.0 (the |
4f6e3300 A |
8 | * "License"). You may not use this file except in compliance with the |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
75b89a82 | 11 | * |
4f6e3300 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
75b89a82 A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
4f6e3300 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
75b89a82 A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * ufs.c - File System Module for UFS. | |
24 | * | |
25 | * Copyright (c) 1998-2002 Apple Computer, Inc. | |
26 | * | |
27 | * DRI: Josh de Cesare | |
28 | */ | |
29 | ||
30 | #include <sl.h> | |
31 | ||
f083c6c3 | 32 | #include "ufs.h" |
75b89a82 A |
33 | #include "ufs_byteorder.h" |
34 | ||
bba600dd A |
35 | #if !defined(MAXNAMLEN) && defined(UFSMAXNAMLEN) |
36 | #define MAXNAMLEN UFSMAXNAMLEN | |
37 | #endif | |
38 | ||
75b89a82 A |
39 | typedef struct dinode Inode, *InodePtr; |
40 | ||
41 | // Private function prototypes | |
42 | ||
43 | static char *ReadBlock(long fragNum, long fragOffset, long length, | |
44 | char *buffer, long cache); | |
45 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time); | |
46 | static long ResolvePathToInode(char *filePath, long *flags, | |
47 | InodePtr fileInode, InodePtr dirInode); | |
48 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum, | |
49 | long *dirIndex, char **name); | |
50 | static long FindFileInDir(char *fileName, long *flags, | |
51 | InodePtr fileInode, InodePtr dirInode); | |
52 | static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset, | |
53 | long length, char *buffer, long cache); | |
bba600dd | 54 | static long ReadFile(InodePtr fileInode, unsigned long *length, void *base, long offset); |
75b89a82 A |
55 | |
56 | #define kDevBlockSize (0x200) // Size of each disk block. | |
57c72a9a | 57 | #define kDiskLabelBlock (15) // Block the DL is in. |
75b89a82 A |
58 | |
59 | #ifdef __i386__ | |
60 | ||
61 | static CICell gCurrentIH; | |
62 | static long long gPartitionBase; | |
57c72a9a | 63 | static char *gULBuf; |
75b89a82 A |
64 | static char *gFSBuf; |
65 | static struct fs *gFS; | |
bba600dd A |
66 | #if !BOOT1 |
67 | static struct ufslabel gUFSLabel; // for UUID | |
68 | #endif | |
75b89a82 A |
69 | static long gBlockSize; |
70 | static long gFragSize; | |
71 | static long gFragsPerBlock; | |
72 | static char *gTempBlock; | |
73 | static char *gTempName; | |
74 | static char *gTempName2; | |
75 | static InodePtr gRootInodePtr; | |
76 | static InodePtr gFileInodePtr; | |
77 | ||
78 | #else /* !__i386__ */ | |
79 | ||
80 | static CICell gCurrentIH; | |
81 | static long long gPartitionBase; | |
82 | static char gDLBuf[8192]; | |
83 | static char gFSBuf[SBSIZE]; | |
84 | static struct fs *gFS; | |
bba600dd A |
85 | #if !BOOT1 |
86 | static struct ufslabel gUFSLabel; // for UUID | |
87 | #endif | |
75b89a82 A |
88 | static long gBlockSize; |
89 | static long gFragSize; | |
90 | static long gFragsPerBlock; | |
91 | static char *gTempBlock; | |
92 | static char gTempName[MAXNAMLEN + 1]; | |
93 | static char gTempName2[MAXNAMLEN + 1]; | |
94 | static Inode _gRootInode; | |
95 | static Inode _gFileInode; | |
96 | static InodePtr gRootInodePtr = &_gRootInode; | |
97 | static InodePtr gFileInodePtr = &_gFileInode; | |
98 | ||
99 | #endif /* !__i386__ */ | |
100 | ||
101 | // Public functions | |
102 | ||
103 | long UFSInitPartition( CICell ih ) | |
104 | { | |
bba600dd A |
105 | #if !BOOT1 |
106 | long ret; | |
107 | #endif | |
108 | ||
75b89a82 A |
109 | if (ih == gCurrentIH) { |
110 | #ifdef __i386__ | |
111 | CacheInit(ih, gBlockSize); | |
112 | #endif | |
113 | return 0; | |
114 | } | |
115 | ||
57c72a9a | 116 | #if !BOOT1 |
75b89a82 | 117 | verbose("UFSInitPartition: %x\n", ih); |
57c72a9a | 118 | #endif |
75b89a82 A |
119 | |
120 | gCurrentIH = 0; | |
121 | ||
122 | #ifdef __i386__ | |
57c72a9a | 123 | if (!gULBuf) gULBuf = (char *) malloc(UFS_LABEL_SIZE); |
75b89a82 A |
124 | if (!gFSBuf) gFSBuf = (char *) malloc(SBSIZE); |
125 | if (!gTempName) gTempName = (char *) malloc(MAXNAMLEN + 1); | |
126 | if (!gTempName2) gTempName2 = (char *) malloc(MAXNAMLEN + 1); | |
127 | if (!gRootInodePtr) gRootInodePtr = (InodePtr) malloc(sizeof(Inode)); | |
128 | if (!gFileInodePtr) gFileInodePtr = (InodePtr) malloc(sizeof(Inode)); | |
57c72a9a | 129 | if (!gULBuf || !gFSBuf || !gTempName || !gTempName2 || |
75b89a82 A |
130 | !gRootInodePtr || !gFileInodePtr) return -1; |
131 | #endif | |
132 | ||
133 | // Assume there is no Disk Label | |
134 | gPartitionBase = 0; | |
135 | ||
bba600dd A |
136 | #if !BOOT1 |
137 | // read the disk label to get the UUID | |
138 | // (rumor has it that UFS headers can be either-endian on disk; hopefully | |
139 | // that isn't true for this UUID field). | |
140 | Seek(ih, gPartitionBase + UFS_LABEL_OFFSET); | |
141 | ret = Read(ih, (long)&gUFSLabel, UFS_LABEL_SIZE); | |
142 | if(ret != 0) | |
143 | bzero(&gUFSLabel, UFS_LABEL_SIZE); | |
144 | #endif /* !BOOT1 */ | |
145 | ||
75b89a82 A |
146 | // Look for the Super Block |
147 | Seek(ih, gPartitionBase + SBOFF); | |
148 | Read(ih, (long)gFSBuf, SBSIZE); | |
149 | ||
150 | gFS = (struct fs *)gFSBuf; | |
151 | byte_swap_superblock(gFS); | |
152 | ||
153 | if (gFS->fs_magic != FS_MAGIC) { | |
57c72a9a | 154 | return -1; |
75b89a82 A |
155 | } |
156 | ||
157 | // Calculate the block size and set up the block cache. | |
158 | gBlockSize = gFS->fs_bsize; | |
159 | gFragSize = gFS->fs_fsize; | |
160 | gFragsPerBlock = gBlockSize / gFragSize; | |
161 | if (gTempBlock != 0) free(gTempBlock); | |
162 | gTempBlock = malloc(gBlockSize); | |
163 | CacheInit(ih, gBlockSize); | |
164 | ||
165 | gCurrentIH = ih; | |
166 | ||
167 | // Read the Root Inode | |
168 | ReadInode(ROOTINO, gRootInodePtr, 0, 0); | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
bba600dd A |
173 | #if !BOOT1 |
174 | ||
175 | long UFSGetUUID(CICell ih, char *uuidStr) | |
176 | { | |
177 | long long uuid = gUFSLabel.ul_uuid; | |
178 | ||
179 | if (UFSInitPartition(ih) == -1) return -1; | |
180 | if (uuid == 0LL) return -1; | |
181 | ||
182 | return CreateUUIDString((uint8_t*)(&uuid), sizeof(uuid), uuidStr); | |
183 | } | |
184 | ||
185 | #endif /* !BOOT1 */ | |
186 | ||
75b89a82 A |
187 | long UFSLoadFile( CICell ih, char * filePath ) |
188 | { | |
57c72a9a A |
189 | return UFSReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0); |
190 | } | |
191 | ||
192 | long UFSReadFile( CICell ih, char * filePath, void * base, unsigned long offset, unsigned long length ) | |
193 | { | |
194 | long ret, flags; | |
75b89a82 | 195 | |
57c72a9a | 196 | #if !BOOT1 |
75b89a82 | 197 | verbose("Loading UFS file: [%s] from %x.\n", filePath, (unsigned)ih); |
57c72a9a | 198 | #endif |
75b89a82 | 199 | |
f083c6c3 A |
200 | if (UFSInitPartition(ih) == -1) return -1; |
201 | ||
75b89a82 A |
202 | // Skip one or two leading '/'. |
203 | if (*filePath == '/') filePath++; | |
204 | if (*filePath == '/') filePath++; | |
205 | ||
206 | ret = ResolvePathToInode(filePath, &flags, gFileInodePtr, gRootInodePtr); | |
207 | if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1; | |
208 | ||
57c72a9a | 209 | ret = ReadFile(gFileInodePtr, &length, base, offset); |
75b89a82 A |
210 | if (ret == -1) return -1; |
211 | ||
212 | return length; | |
213 | } | |
214 | ||
57c72a9a A |
215 | #ifndef BOOT1 |
216 | ||
75b89a82 | 217 | long UFSGetDirEntry( CICell ih, char * dirPath, long * dirIndex, |
57c72a9a A |
218 | char ** name, long * flags, long * time, |
219 | FinderInfo * finderInfo, long * infoValid) | |
75b89a82 A |
220 | { |
221 | long ret, fileInodeNum, dirFlags; | |
222 | Inode tmpInode; | |
223 | ||
224 | if (UFSInitPartition(ih) == -1) return -1; | |
225 | ||
57c72a9a A |
226 | if (infoValid) *infoValid = 0; |
227 | ||
75b89a82 A |
228 | // Skip a leading '/' if present |
229 | if (*dirPath == '/') dirPath++; | |
230 | if (*dirPath == '/') dirPath++; | |
231 | ||
232 | ret = ResolvePathToInode(dirPath, &dirFlags, gFileInodePtr, gRootInodePtr); | |
233 | if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory)) | |
234 | return -1; | |
235 | ||
236 | ret = ReadDirEntry(gFileInodePtr, &fileInodeNum, dirIndex, name); | |
237 | if (ret != 0) return ret; | |
238 | ||
239 | ReadInode(fileInodeNum, &tmpInode, flags, time); | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
57c72a9a A |
244 | void |
245 | UFSGetDescription(CICell ih, char *str, long strMaxLen) | |
246 | { | |
247 | if (UFSInitPartition(ih) == -1) { return; } | |
248 | ||
249 | struct ufslabel *ul; | |
250 | ||
251 | // Look for the Disk Label | |
252 | Seek(ih, 1ULL * UFS_LABEL_OFFSET); | |
253 | Read(ih, (long)gULBuf, UFS_LABEL_SIZE); | |
254 | ||
255 | ul = (struct ufslabel *)gULBuf; | |
256 | ||
257 | unsigned char magic_bytes[] = UFS_LABEL_MAGIC; | |
258 | int i; | |
259 | unsigned char *p = (unsigned char *)&ul->ul_magic; | |
260 | ||
261 | for (i=0; i<sizeof(magic_bytes); i++, p++) { | |
262 | if (*p != magic_bytes[i]) | |
263 | return; | |
264 | } | |
bba600dd | 265 | strncpy(str, (const char *)ul->ul_name, strMaxLen); |
57c72a9a A |
266 | } |
267 | ||
268 | long | |
269 | UFSGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock) | |
270 | { | |
271 | long ret, flags; | |
272 | ||
273 | if (UFSInitPartition(ih) == -1) return -1; | |
274 | ||
275 | // Skip one or two leading '/'. | |
276 | if (*filePath == '/') filePath++; | |
277 | if (*filePath == '/') filePath++; | |
278 | ||
279 | ret = ResolvePathToInode(filePath, &flags, gFileInodePtr, gRootInodePtr); | |
280 | if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1; | |
281 | ||
282 | *firstBlock = (gPartitionBase + 1ULL * gFileInodePtr->di_db[0] * gBlockSize) / 512ULL; | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | ||
288 | #endif /* !BOOT1 */ | |
289 | ||
75b89a82 A |
290 | // Private functions |
291 | ||
292 | static char * ReadBlock( long fragNum, long blockOffset, long length, | |
293 | char * buffer, long cache ) | |
294 | { | |
295 | long long offset; | |
296 | long blockNum; | |
297 | ||
298 | blockNum = fragNum / gFragsPerBlock; | |
299 | fragNum -= blockNum * gFragsPerBlock; | |
300 | ||
301 | blockOffset += fragNum * gFragSize; | |
302 | ||
303 | offset = gPartitionBase + 1ULL * blockNum * gBlockSize; | |
304 | ||
305 | if (cache && ((blockOffset + length) <= gBlockSize)) { | |
306 | CacheRead(gCurrentIH, gTempBlock, offset, gBlockSize, 1); | |
307 | if (buffer != 0) bcopy(gTempBlock + blockOffset, buffer, length); | |
308 | else buffer = gTempBlock + blockOffset; | |
309 | } else { | |
310 | offset += blockOffset; | |
311 | CacheRead(gCurrentIH, buffer, offset, length, 0); | |
312 | } | |
313 | ||
314 | return buffer; | |
315 | } | |
316 | ||
317 | static long ReadInode( long inodeNum, InodePtr inode, long * flags, long * time ) | |
318 | { | |
319 | long fragNum = ino_to_fsba(gFS, inodeNum); | |
320 | long blockOffset = ino_to_fsbo(gFS, inodeNum) * sizeof(Inode); | |
321 | ||
322 | ReadBlock(fragNum, blockOffset, sizeof(Inode), (char *)inode, 1); | |
323 | byte_swap_dinode_in(inode); | |
324 | ||
325 | if (time != 0) *time = inode->di_mtime; | |
326 | ||
327 | if (flags != 0) { | |
328 | switch (inode->di_mode & IFMT) { | |
329 | case IFREG: *flags = kFileTypeFlat; break; | |
330 | case IFDIR: *flags = kFileTypeDirectory; break; | |
331 | case IFLNK: *flags = kFileTypeLink; break; | |
332 | default : *flags = kFileTypeUnknown; break; | |
333 | } | |
334 | ||
335 | *flags |= inode->di_mode & kPermMask; | |
336 | ||
337 | if (inode->di_uid != 0) *flags |= kOwnerNotRoot; | |
338 | } | |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
343 | static long ResolvePathToInode( char * filePath, long * flags, | |
344 | InodePtr fileInode, InodePtr dirInode ) | |
345 | { | |
346 | char * restPath; | |
347 | long ret, cnt; | |
348 | ||
349 | // if filePath is empty the we want this directory. | |
350 | if (*filePath == '\0') { | |
351 | bcopy((char *)dirInode, (char *)fileInode, sizeof(Inode)); | |
352 | return 0; | |
353 | } | |
354 | ||
355 | // Copy the file name to gTempName | |
356 | cnt = 0; | |
357 | while ((filePath[cnt] != '/') && (filePath[cnt] != '\0')) cnt++; | |
f083c6c3 | 358 | strlcpy(gTempName, filePath, cnt+1); |
75b89a82 A |
359 | |
360 | // Move restPath to the right place. | |
361 | if (filePath[cnt] != '\0') cnt++; | |
362 | restPath = filePath + cnt; | |
363 | ||
364 | // gTempName is a name in the current Dir. | |
365 | // restPath is the rest of the path if any. | |
366 | ||
367 | ret = FindFileInDir(gTempName, flags, fileInode, dirInode); | |
368 | if (ret == -1) return -1; | |
369 | ||
370 | if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory)) | |
371 | ret = ResolvePathToInode(restPath, flags, fileInode, fileInode); | |
372 | ||
373 | return ret; | |
374 | } | |
375 | ||
376 | static long ReadDirEntry( InodePtr dirInode, long * fileInodeNum, | |
377 | long * dirIndex, char ** name ) | |
378 | { | |
379 | struct direct *dir; | |
380 | char *buffer; | |
381 | long index; | |
382 | long dirBlockNum, dirBlockOffset; | |
383 | ||
384 | while (1) { | |
385 | index = *dirIndex; | |
386 | ||
387 | dirBlockOffset = index % DIRBLKSIZ; | |
388 | dirBlockNum = index / DIRBLKSIZ; | |
389 | ||
390 | buffer = ReadFileBlock(dirInode, dirBlockNum, 0, DIRBLKSIZ, 0, 1); | |
391 | if (buffer == 0) return -1; | |
392 | ||
393 | dir = (struct direct *)(buffer + dirBlockOffset); | |
394 | byte_swap_dir_block_in((char *)dir, 1); | |
395 | ||
396 | *dirIndex += dir->d_reclen; | |
397 | ||
398 | if (dir->d_ino != 0) break; | |
399 | ||
400 | if (dirBlockOffset != 0) return -1; | |
401 | } | |
402 | ||
403 | *fileInodeNum = dir->d_ino; | |
f083c6c3 | 404 | *name = strlcpy(gTempName2, dir->d_name, dir->d_namlen+1); |
75b89a82 A |
405 | |
406 | return 0; | |
407 | } | |
408 | ||
409 | static long FindFileInDir( char * fileName, long * flags, | |
410 | InodePtr fileInode, InodePtr dirInode ) | |
411 | { | |
412 | long ret, inodeNum, index = 0; | |
413 | char *name; | |
414 | ||
415 | while (1) { | |
416 | ret = ReadDirEntry(dirInode, &inodeNum, &index, &name); | |
417 | if (ret == -1) return -1; | |
418 | ||
419 | if (strcmp(fileName, name) == 0) break; | |
420 | } | |
421 | ||
422 | ReadInode(inodeNum, fileInode, flags, 0); | |
423 | ||
424 | return 0; | |
425 | } | |
426 | ||
427 | static char * ReadFileBlock( InodePtr fileInode, long fragNum, long blockOffset, | |
428 | long length, char * buffer, long cache ) | |
429 | { | |
430 | long fragCount, blockNum; | |
431 | long diskFragNum, indFragNum, indBlockOff, refsPerBlock; | |
432 | char *indBlock; | |
433 | ||
434 | fragCount = (fileInode->di_size + gFragSize - 1) / gFragSize; | |
435 | if (fragNum >= fragCount) return 0; | |
436 | ||
437 | refsPerBlock = gBlockSize / sizeof(ufs_daddr_t); | |
438 | ||
439 | blockNum = fragNum / gFragsPerBlock; | |
440 | fragNum -= blockNum * gFragsPerBlock; | |
441 | ||
442 | // Get Direct Block Number. | |
443 | if (blockNum < NDADDR) { | |
444 | diskFragNum = fileInode->di_db[blockNum]; | |
445 | } else { | |
446 | blockNum -= NDADDR; | |
447 | ||
448 | // Get Single Indirect Fragment Number. | |
449 | if (blockNum < refsPerBlock) { | |
450 | indFragNum = fileInode->di_ib[0]; | |
451 | } else { | |
452 | blockNum -= refsPerBlock; | |
453 | ||
454 | // Get Double Indirect Fragment Number. | |
455 | if (blockNum < (refsPerBlock * refsPerBlock)) { | |
456 | indFragNum = fileInode->di_ib[1]; | |
457 | } else { | |
458 | blockNum -= refsPerBlock * refsPerBlock; | |
459 | ||
460 | // Get Triple Indirect Fragment Number. | |
461 | indFragNum = fileInode->di_ib[2]; | |
462 | ||
463 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
464 | indBlockOff = blockNum / (refsPerBlock * refsPerBlock); | |
465 | blockNum %= (refsPerBlock * refsPerBlock); | |
466 | indFragNum = SWAP_BE32(((ufs_daddr_t *)indBlock)[indBlockOff]); | |
467 | } | |
468 | ||
469 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
470 | indBlockOff = blockNum / refsPerBlock; | |
471 | blockNum %= refsPerBlock; | |
472 | indFragNum = SWAP_BE32(((ufs_daddr_t *)indBlock)[indBlockOff]); | |
473 | } | |
474 | ||
475 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
476 | diskFragNum = SWAP_BE32(((ufs_daddr_t *)indBlock)[blockNum]); | |
477 | } | |
478 | ||
479 | buffer = ReadBlock(diskFragNum+fragNum, blockOffset, length, buffer, cache); | |
480 | ||
481 | return buffer; | |
482 | } | |
483 | ||
bba600dd | 484 | static long ReadFile( InodePtr fileInode, unsigned long * length, void * base, long offset ) |
75b89a82 | 485 | { |
57c72a9a A |
486 | long bytesLeft, curSize, curFrag; |
487 | char *buffer, *curAddr = (char *)base; | |
75b89a82 | 488 | |
57c72a9a | 489 | bytesLeft = fileInode->di_size; |
75b89a82 | 490 | |
57c72a9a A |
491 | if (offset > bytesLeft) { |
492 | printf("Offset is too large.\n"); | |
493 | return -1; | |
494 | } | |
495 | ||
496 | if ((*length == 0) || ((offset + *length) > bytesLeft)) { | |
497 | *length = bytesLeft - offset; | |
498 | } | |
75b89a82 | 499 | |
57c72a9a | 500 | if (bytesLeft > kLoadSize) { |
75b89a82 A |
501 | printf("File is too large.\n"); |
502 | return -1; | |
503 | } | |
504 | ||
57c72a9a A |
505 | bytesLeft = *length; |
506 | curFrag = (offset / gBlockSize) * gFragsPerBlock; | |
507 | offset %= gBlockSize; | |
508 | ||
75b89a82 | 509 | while (bytesLeft) { |
57c72a9a A |
510 | curSize = gBlockSize; |
511 | if (curSize > bytesLeft) curSize = bytesLeft; | |
bba600dd | 512 | if ((offset + curSize) > gBlockSize) curSize = (gBlockSize - offset); |
75b89a82 | 513 | |
57c72a9a | 514 | buffer = ReadFileBlock(fileInode, curFrag, offset, curSize, curAddr, 0); |
75b89a82 A |
515 | if (buffer == 0) break; |
516 | ||
57c72a9a A |
517 | if (offset != 0) offset = 0; |
518 | ||
75b89a82 A |
519 | curFrag += gFragsPerBlock; |
520 | curAddr += curSize; | |
521 | bytesLeft -= curSize; | |
522 | } | |
523 | ||
524 | return bytesLeft; | |
525 | } |