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