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