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