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