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