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