]> git.saurik.com Git - apple/bootx.git/blame - bootx.tproj/fs.subproj/ufs.c
BootX-81.tar.gz
[apple/bootx.git] / bootx.tproj / fs.subproj / ufs.c
CommitLineData
04fee52e 1/*
873b6fa6 2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
04fee52e 3 *
873b6fa6 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
04fee52e 5 *
873b6fa6
A
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.
04fee52e 14 *
873b6fa6
A
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
04fee52e
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
873b6fa6
A
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.
04fee52e 25 *
873b6fa6 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
04fee52e 27 */
873b6fa6 28
04fee52e
A
29/*
30 * ufs.c - File System Module for UFS.
31 *
8be739c0 32 * Copyright (c) 1998-2004 Apple Computer, Inc.
04fee52e
A
33 *
34 * DRI: Josh de Cesare
35 */
36
37#include <sl.h>
366defd1 38
04fee52e
A
39#include "ufs_byteorder.h"
40
95a2ba82
A
41#include <dirent.h> // for MAXNAMLEN
42
04fee52e
A
43typedef struct dinode Inode, *InodePtr;
44
45// Private function prototypes
46
47static char *ReadBlock(long fragNum, long fragOffset, long length,
48 char *buffer, long cache);
366defd1 49static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time);
04fee52e
A
50static long ResolvePathToInode(char *filePath, long *flags,
51 InodePtr fileInode, InodePtr dirInode);
52static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
873b6fa6 53 unsigned long *dirIndex, char **name);
04fee52e
A
54static long FindFileInDir(char *fileName, long *flags,
55 InodePtr fileInode, InodePtr dirInode);
56static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset,
57 long length, char *buffer, long cache);
8be739c0
A
58static long ReadFile(InodePtr fileInode, long *length,
59 void *base, long offset);
04fee52e
A
60
61
04fee52e
A
62static CICell gCurrentIH;
63static long long gPartitionBase;
04fee52e
A
64static char gFSBuf[SBSIZE];
65static struct fs *gFS;
8be739c0 66static struct ufslabel gUFSLabel; // for UUID
04fee52e 67static long gBlockSize;
366defd1 68static long gBlockSizeOld;
04fee52e
A
69static long gFragSize;
70static long gFragsPerBlock;
71static char *gTempBlock;
72static char gTempName[MAXNAMLEN + 1];
73static char gTempName2[MAXNAMLEN + 1];
74static Inode gRootInode;
75static Inode gFileInode;
76
77// Public functions
78
79long UFSInitPartition(CICell ih)
80{
8be739c0
A
81 int ret;
82
04fee52e
A
83 if (ih == gCurrentIH) return 0;
84
85 printf("UFSInitPartition: %x\n", ih);
86
87 gCurrentIH = 0;
8be739c0
A
88
89 // Assume UFS starts at the beginning of the device
04fee52e
A
90 gPartitionBase = 0;
91
8be739c0
A
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
04fee52e
A
100 // Look for the Super Block
101 Seek(ih, gPartitionBase + SBOFF);
102 Read(ih, (long)gFSBuf, SBSIZE);
103
104 gFS = (struct fs *)gFSBuf;
8be739c0 105//printf("looking for UFS magic ... \n");
04fee52e 106 if (gFS->fs_magic != FS_MAGIC) {
71019aa0 107 return -1;
04fee52e 108 }
8be739c0 109//printf("continuing w/UFS\n");
04fee52e
A
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;
366defd1
A
115
116 if (gBlockSizeOld <= gBlockSize) {
117 gTempBlock = AllocateBootXMemory(gBlockSize);
118 }
119
04fee52e
A
120 CacheInit(ih, gBlockSize);
121
366defd1
A
122 gBlockSizeOld = gBlockSize;
123
04fee52e
A
124 gCurrentIH = ih;
125
126 // Read the Root Inode
366defd1 127 ReadInode(ROOTINO, &gRootInode, 0, 0);
04fee52e
A
128
129 return 0;
130}
131
132
8be739c0
A
133long 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
04fee52e
A
143long UFSLoadFile(CICell ih, char *filePath)
144{
8be739c0
A
145 return UFSReadFile(ih, filePath, (void *)kLoadAddr, 0, 0);
146}
147
148long UFSReadFile(CICell ih, char *filePath, void *base,
149 unsigned long offset, unsigned long length)
150{
151 long ret, flags;
04fee52e
A
152
153 if (UFSInitPartition(ih) == -1) return -1;
154
8be739c0
A
155 printf("%s UFS file: [%s] from %x.\n",
156 (((offset == 0) && (length == 0)) ? "Loading" : "Reading"),
157 filePath, ih);
04fee52e
A
158
159 // Skip one or two leading '\'.
160 if (*filePath == '\\') filePath++;
161 if (*filePath == '\\') filePath++;
162 ret = ResolvePathToInode(filePath, &flags, &gFileInode, &gRootInode);
366defd1
A
163 if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;
164
8be739c0
A
165 if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) {
166 printf("%s: permissions incorrect\n", filePath);
167 return -1;
168 }
04fee52e 169
8be739c0 170 ret = ReadFile(&gFileInode, &length, base, offset);
04fee52e
A
171 if (ret == -1) return -1;
172
173 return length;
174}
175
176
873b6fa6 177long UFSGetDirEntry(CICell ih, char *dirPath, unsigned long *dirIndex,
04fee52e
A
178 char **name, long *flags, long *time)
179{
366defd1
A
180 long ret, fileInodeNum, dirFlags;
181 Inode tmpInode;
04fee52e
A
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);
366defd1
A
189 if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory))
190 return -1;
04fee52e 191
366defd1
A
192 ret = ReadDirEntry(&gFileInode, &fileInodeNum, dirIndex, name);
193 if (ret != 0) return ret;
04fee52e 194
366defd1
A
195 ReadInode(fileInodeNum, &tmpInode, flags, time);
196
197 return 0;
04fee52e
A
198}
199
200// Private functions
201
202static 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
366defd1 228static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time)
04fee52e
A
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
366defd1
A
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
8be739c0
A
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 }
366defd1
A
253 }
254
04fee52e
A
255 return 0;
256}
257
258
259static 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
366defd1 286 if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory))
04fee52e
A
287 ret = ResolvePathToInode(restPath, flags, fileInode, fileInode);
288
289 return ret;
290}
291
292
293static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
873b6fa6 294 unsigned long *dirIndex, char **name)
04fee52e
A
295{
296 struct direct *dir;
297 char *buffer;
366defd1 298 long index;
04fee52e 299 long dirBlockNum, dirBlockOffset;
04fee52e 300
366defd1
A
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 }
04fee52e 317
04fee52e
A
318 *fileInodeNum = dir->d_ino;
319 *name = strncpy(gTempName2, dir->d_name, dir->d_namlen);
320
04fee52e
A
321 return 0;
322}
323
324
325static long FindFileInDir(char *fileName, long *flags,
326 InodePtr fileInode, InodePtr dirInode)
327{
873b6fa6
A
328 long ret, inodeNum;
329 unsigned long index = 0;
04fee52e
A
330 char *name;
331
332 while (1) {
366defd1 333 ret = ReadDirEntry(dirInode, &inodeNum, &index, &name);
04fee52e
A
334 if (ret == -1) return -1;
335
04fee52e
A
336 if (strcmp(fileName, name) == 0) break;
337 }
338
366defd1 339 ReadInode(inodeNum, fileInode, flags, 0);
04fee52e
A
340
341 return 0;
342}
343
344
345static 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
8be739c0 403static long ReadFile(InodePtr fileInode, long *length, void *base, long offset)
04fee52e 404{
8be739c0
A
405 long bytesLeft, curSize, curFrag;
406 char *buffer, *curAddr = (char *)base;
407
408 bytesLeft = fileInode->di_size;
04fee52e 409
8be739c0
A
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 }
04fee52e
A
418
419 if (*length > kLoadSize) {
420 printf("File is too large.\n");
421 return -1;
422 }
423
8be739c0
A
424 bytesLeft = *length;
425 curFrag = (offset / gBlockSize) * gFragsPerBlock;
426 offset %= gBlockSize;
427
04fee52e 428 while (bytesLeft) {
8be739c0
A
429 curSize = gBlockSize;
430 if (curSize > bytesLeft) curSize = bytesLeft;
431 if (offset != 0) curSize -= offset;
04fee52e 432
8be739c0 433 buffer = ReadFileBlock(fileInode, curFrag, offset, curSize, curAddr, 0);
04fee52e
A
434 if (buffer == 0) break;
435
8be739c0
A
436 if (offset != 0) offset = 0;
437
04fee52e
A
438 curFrag += gFragsPerBlock;
439 curAddr += curSize;
440 bytesLeft -= curSize;
441 }
442
443 return bytesLeft;
444}