]> git.saurik.com Git - apple/bootx.git/blob - bootx.tproj/fs.subproj/ext2fs.c
BootX-81.tar.gz
[apple/bootx.git] / bootx.tproj / fs.subproj / ext2fs.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 * ext2.c - File System Module for Ext2.
31 *
32 * Copyright (c) 1999-2002 Apple Computer, Inc.
33 *
34 * DRI: Josh de Cesare
35 */
36
37 #include <sl.h>
38
39 #include "ext2fs.h"
40 #include "ext2fs_dinode.h"
41 #include "ext2fs_dir.h"
42
43 typedef struct ext2fs_dinode Inode, *InodePtr;
44
45 // Private function prototypes
46
47 static long HowMany(long bufferSize, long unitSize);
48 static char *ReadBlock(long blockNum, long blockOffset, long length,
49 char *buffer, long cache);
50 static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time);
51 static long ResolvePathToInode(char *filePath, long *flags,
52 InodePtr fileInode, InodePtr dirInode);
53 static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
54 unsigned long *dirIndex, char **name);
55 static long FindFileInDir(char *fileName, long *flags,
56 InodePtr fileInode, InodePtr dirInode);
57 static char *ReadFileBlock(InodePtr fileInode, long blockNum, long blockOffset,
58 long length, char *buffer, long cache);
59 static long ReadFile(InodePtr fileInode, long *length);
60
61
62 static CICell gCurrentIH;
63 static char gFSBuf[SBSIZE * 2];
64 static struct m_ext2fs *gFS;
65 static long gBlockSize;
66 static long gBlockSizeOld;
67 static char *gTempBlock;
68 static char gTempName[EXT2FS_MAXNAMLEN + 1];
69 static char gTempName2[EXT2FS_MAXNAMLEN + 1];
70 static Inode gRootInode;
71 static Inode gFileInode;
72
73 // Public functions
74
75 long Ext2InitPartition(CICell ih)
76 {
77 long cnt, gdPerBlock;
78
79 if (ih == gCurrentIH) return 0;
80
81 printf("Ext2InitPartition: %x\n", ih);
82
83 gCurrentIH = 0;
84
85 // Read for the Super Block.
86 Seek(ih, SBOFF);
87 Read(ih, (long)gFSBuf, SBSIZE);
88
89 gFS = (struct m_ext2fs *)gFSBuf;
90 e2fs_sb_bswap(&gFS->e2fs, &gFS->e2fs);
91 if (gFS->e2fs.e2fs_magic != E2FS_MAGIC) return -1;
92
93 // Calculate the block size and set up the block cache.
94 gBlockSize = 1024 << gFS->e2fs.e2fs_log_bsize;
95 if (gBlockSizeOld <= gBlockSize) {
96 gTempBlock = AllocateBootXMemory(gBlockSize);
97 }
98 CacheInit(ih, gBlockSize);
99
100 gBlockSizeOld = gBlockSize;
101
102 gCurrentIH = ih;
103
104 gdPerBlock = gBlockSize / sizeof(struct ext2_gd);
105
106 // Fill in the in memory super block fields.
107 gFS->e2fs_bsize = 1024 << gFS->e2fs.e2fs_log_bsize;
108 gFS->e2fs_bshift = LOG_MINBSIZE + gFS->e2fs.e2fs_log_bsize;
109 gFS->e2fs_qbmask = gFS->e2fs_bsize - 1;
110 gFS->e2fs_bmask = ~gFS->e2fs_qbmask;
111 gFS->e2fs_fsbtodb = gFS->e2fs.e2fs_log_bsize + 1;
112 gFS->e2fs_ncg = HowMany(gFS->e2fs.e2fs_bcount - gFS->e2fs.e2fs_first_dblock,
113 gFS->e2fs.e2fs_bpg);
114 gFS->e2fs_ngdb = HowMany(gFS->e2fs_ncg, gdPerBlock);
115 gFS->e2fs_ipb = gFS->e2fs_bsize / EXT2_DINODE_SIZE;
116 gFS->e2fs_itpg = gFS->e2fs.e2fs_ipg / gFS->e2fs_ipb;
117 gFS->e2fs_gd = AllocateBootXMemory(gFS->e2fs_ngdb * gFS->e2fs_bsize);
118
119 // Read the summary information from disk.
120 for (cnt = 0; cnt < gFS->e2fs_ngdb; cnt++) {
121 ReadBlock(((gBlockSize > 1024) ? 0 : 1) + cnt + 1, 0, gBlockSize,
122 (char *)&gFS->e2fs_gd[gdPerBlock * cnt], 0);
123 e2fs_cg_bswap(&gFS->e2fs_gd[gdPerBlock * cnt],
124 &gFS->e2fs_gd[gdPerBlock * cnt], gBlockSize);
125 }
126
127 // Read the Root Inode
128 ReadInode(EXT2_ROOTINO, &gRootInode, 0, 0);
129
130 return 0;
131 }
132
133
134 long Ext2LoadFile(CICell ih, char *filePath)
135 {
136 long ret, length, flags;
137
138 if (Ext2InitPartition(ih) == -1) return -1;
139
140 printf("Loading Ext2 file: [%s] from %x.\n", filePath, ih);
141
142 // Skip one or two leading '\'.
143 if (*filePath == '\\') filePath++;
144 if (*filePath == '\\') filePath++;
145 ret = ResolvePathToInode(filePath, &flags, &gFileInode, &gRootInode);
146 if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;
147
148 if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;
149
150 ret = ReadFile(&gFileInode, &length);
151 if (ret != 0) return -1;
152
153 return length;
154 }
155
156 long Ext2GetDirEntry(CICell ih, char *dirPath, unsigned long *dirIndex,
157 char **name, long *flags, long *time)
158 {
159 long ret, fileInodeNum, dirFlags;
160 Inode tmpInode;
161
162 if (Ext2InitPartition(ih) == -1) return -1;
163
164 // Skip a leading '\' if present
165 if (dirPath[0] == '\\') dirPath++;
166 ret = ResolvePathToInode(dirPath, &dirFlags, &gFileInode, &gRootInode);
167 if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory))
168 return -1;
169
170 ret = ReadDirEntry(&gFileInode, &fileInodeNum, dirIndex, name);
171 if (ret != 0) return ret;
172
173 ReadInode(fileInodeNum, &tmpInode, flags, time);
174
175 return 0;
176 }
177
178 // XX no support in AppleFileSystemDriver yet
179 long Ext2GetUUID(CICell ih, char *uuidStr)
180 {
181 uint8_t *uuid = gFS->e2fs.e2fs_uuid;
182
183 if (Ext2InitPartition(ih) == -1) return -1;
184
185 return CreateUUIDString(uuid, sizeof(gFS->e2fs.e2fs_uuid), uuidStr);
186 }
187
188 // Private functions
189
190 static long HowMany(long bufferSize, long unitSize)
191 {
192 return (bufferSize + unitSize - 1) / unitSize;
193 }
194
195
196 static char *ReadBlock(long blockNum, long blockOffset, long length,
197 char *buffer, long cache)
198 {
199 long long offset;
200
201 offset = 1ULL * blockNum * gBlockSize;
202
203 if (cache && ((blockOffset + length) <= gBlockSize)) {
204 CacheRead(gCurrentIH, gTempBlock, offset, gBlockSize, 1);
205 if (buffer != 0) bcopy(gTempBlock + blockOffset, buffer, length);
206 else buffer = gTempBlock + blockOffset;
207 } else {
208 offset += blockOffset;
209 CacheRead(gCurrentIH, buffer, offset, length, 0);
210 }
211
212 return buffer;
213 }
214
215
216 static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time)
217 {
218 long blockNum = ino_to_fsba(gFS, inodeNum);
219 long blockOffset = ino_to_fsbo(gFS, inodeNum) * sizeof(Inode);
220
221 ReadBlock(blockNum, blockOffset, sizeof(Inode), (char *)inode, 1);
222 e2fs_i_bswap(inode, inode);
223
224 if (time != 0) *time = inode->e2di_mtime;
225
226 if (flags != 0) {
227 switch (inode->e2di_mode & EXT2_IFMT) {
228 case EXT2_IFREG: *flags = kFileTypeFlat; break;
229 case EXT2_IFDIR: *flags = kFileTypeDirectory; break;
230 case EXT2_IFLNK: *flags = kFileTypeLink; break;
231 default : *flags = kFileTypeUnknown; break;
232 }
233
234 *flags |= inode->e2di_mode & kPermMask;
235
236 if (inode->e2di_uid != 0) *flags |= kOwnerNotRoot;
237 }
238
239 return 0;
240 }
241
242
243 static long ResolvePathToInode(char *filePath, long *flags,
244 InodePtr fileInode, InodePtr dirInode)
245 {
246 char *restPath;
247 long ret, cnt;
248
249 // if filePath is empty the we want this directory.
250 if (*filePath == '\0') {
251 bcopy((char *)dirInode, (char *)fileInode, sizeof(Inode));
252 return 0;
253 }
254
255 // Copy the file name to gTempName
256 cnt = 0;
257 while ((filePath[cnt] != '\\') && (filePath[cnt] != '\0')) cnt++;
258 strncpy(gTempName, filePath, cnt);
259
260 // Move restPath to the right place.
261 if (filePath[cnt] != '\0') cnt++;
262 restPath = filePath + cnt;
263
264 // gTempName is a name in the current Dir.
265 // restPath is the rest of the path if any.
266
267 ret = FindFileInDir(gTempName, flags, fileInode, dirInode);
268 if (ret == -1) return -1;
269
270 if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory))
271 ret = ResolvePathToInode(restPath, flags, fileInode, fileInode);
272
273 return ret;
274 }
275
276
277 static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
278 unsigned long *dirIndex, char **name)
279 {
280 struct ext2fs_direct *dir;
281 char *buffer;
282 long offset, index;
283 long blockNum, inodeNum;
284
285 while (1) {
286 index = *dirIndex;
287
288 offset = index % gBlockSize;
289 blockNum = index / gBlockSize;
290
291 buffer = ReadFileBlock(dirInode, blockNum, 0, gBlockSize, 0, 1);
292 if (buffer == 0) return -1;
293
294 dir = (struct ext2fs_direct *)(buffer + offset);
295 *dirIndex += bswap16(dir->e2d_reclen);
296
297 inodeNum = bswap32(dir->e2d_ino);
298 if (inodeNum != 0) break;
299
300 if (offset != 0) return -1;
301 }
302
303 *fileInodeNum = inodeNum;
304 *name = strncpy(gTempName2, dir->e2d_name, dir->e2d_namlen);
305
306 return 0;
307 }
308
309
310 static long FindFileInDir(char *fileName, long *flags,
311 InodePtr fileInode, InodePtr dirInode)
312 {
313 long ret, inodeNum;
314 unsigned long index = 0;
315 char *name;
316
317 while (1) {
318 ret = ReadDirEntry(dirInode, &inodeNum, &index, &name);
319 if (ret == -1) return -1;
320
321 if (strcmp(fileName, name) == 0) break;
322 }
323
324 ReadInode(inodeNum, fileInode, flags, 0);
325
326 return 0;
327 }
328
329
330 static char *ReadFileBlock(InodePtr fileInode, long blockNum, long blockOffset,
331 long length, char *buffer, long cache)
332 {
333 long diskBlockNum, indBlockNum, indBlockOff, refsPerBlock;
334 char *indBlock;
335
336 if (blockNum >= fileInode->e2di_nblock) return 0;
337
338 refsPerBlock = gBlockSize / sizeof(u_int32_t);
339
340 // Get Direct Block Number.
341 if (blockNum < NDADDR) {
342 diskBlockNum = bswap32(fileInode->e2di_blocks[blockNum]);
343 } else {
344 blockNum -= NDADDR;
345
346 // Get Single Indirect Block Number.
347 if (blockNum < refsPerBlock) {
348 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR]);
349 } else {
350 blockNum -= refsPerBlock;
351
352 // Get Double Indirect Block Number.
353 if (blockNum < (refsPerBlock * refsPerBlock)) {
354 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 1]);
355 } else {
356 blockNum -= refsPerBlock * refsPerBlock;
357
358 // Get Triple Indirect Block Number.
359 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 2]);
360
361 indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1);
362 indBlockOff = blockNum / (refsPerBlock * refsPerBlock);
363 blockNum %= (refsPerBlock * refsPerBlock);
364 indBlockNum = bswap32(((u_int32_t *)indBlock)[indBlockOff]);
365 }
366
367 indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1);
368 indBlockOff = blockNum / refsPerBlock;
369 blockNum %= refsPerBlock;
370 indBlockNum = bswap32(((u_int32_t *)indBlock)[indBlockOff]);
371 }
372
373 indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1);
374 diskBlockNum = bswap32(((u_int32_t *)indBlock)[blockNum]);
375 }
376
377 buffer = ReadBlock(diskBlockNum, blockOffset, length, buffer, cache);
378
379 return buffer;
380 }
381
382 static long ReadFile(InodePtr fileInode, long *length)
383 {
384 long bytesLeft, curSize, curBlock = 0;
385 char *buffer, *curAddr = (char *)kLoadAddr;
386
387 bytesLeft = *length = fileInode->e2di_size;
388
389 if (*length > kLoadSize) {
390 printf("File is too large.\n");
391 return -1;
392 }
393
394 while (bytesLeft) {
395 if (bytesLeft > gBlockSize) curSize = gBlockSize;
396 else curSize = bytesLeft;
397
398 buffer = ReadFileBlock(fileInode, curBlock, 0, curSize, curAddr, 0);
399 if (buffer == 0) break;
400
401 curBlock++;
402 curAddr += curSize;
403 bytesLeft -= curSize;
404 }
405
406 return bytesLeft;
407 }