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