]> git.saurik.com Git - apple/boot.git/blame - i386/libsaio/ufs.c
boot-132.tar.gz
[apple/boot.git] / i386 / libsaio / ufs.c
CommitLineData
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
39typedef struct dinode Inode, *InodePtr;
40
41// Private function prototypes
42
43static char *ReadBlock(long fragNum, long fragOffset, long length,
44 char *buffer, long cache);
45static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time);
46static long ResolvePathToInode(char *filePath, long *flags,
47 InodePtr fileInode, InodePtr dirInode);
48static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
49 long *dirIndex, char **name);
50static long FindFileInDir(char *fileName, long *flags,
51 InodePtr fileInode, InodePtr dirInode);
52static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset,
53 long length, char *buffer, long cache);
bba600dd 54static 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
61static CICell gCurrentIH;
62static long long gPartitionBase;
57c72a9a 63static char *gULBuf;
75b89a82
A
64static char *gFSBuf;
65static struct fs *gFS;
bba600dd
A
66#if !BOOT1
67static struct ufslabel gUFSLabel; // for UUID
68#endif
75b89a82
A
69static long gBlockSize;
70static long gFragSize;
71static long gFragsPerBlock;
72static char *gTempBlock;
73static char *gTempName;
74static char *gTempName2;
75static InodePtr gRootInodePtr;
76static InodePtr gFileInodePtr;
77
78#else /* !__i386__ */
79
80static CICell gCurrentIH;
81static long long gPartitionBase;
82static char gDLBuf[8192];
83static char gFSBuf[SBSIZE];
84static struct fs *gFS;
bba600dd
A
85#if !BOOT1
86static struct ufslabel gUFSLabel; // for UUID
87#endif
75b89a82
A
88static long gBlockSize;
89static long gFragSize;
90static long gFragsPerBlock;
91static char *gTempBlock;
92static char gTempName[MAXNAMLEN + 1];
93static char gTempName2[MAXNAMLEN + 1];
94static Inode _gRootInode;
95static Inode _gFileInode;
96static InodePtr gRootInodePtr = &_gRootInode;
97static InodePtr gFileInodePtr = &_gFileInode;
98
99#endif /* !__i386__ */
100
101// Public functions
102
103long 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
175long 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
187long UFSLoadFile( CICell ih, char * filePath )
188{
57c72a9a
A
189 return UFSReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0);
190}
191
192long 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 217long 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
244void
245UFSGetDescription(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
268long
269UFSGetFileBlock(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
292static 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
317static 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
343static 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
376static 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
409static 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
427static 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 484static 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}