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