]> git.saurik.com Git - apple/bootx.git/blame - bootx.tproj/fs.subproj/ext2fs.c
BootX-81.tar.gz
[apple/bootx.git] / bootx.tproj / fs.subproj / ext2fs.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 * ext2.c - File System Module for Ext2.
31 *
366defd1 32 * Copyright (c) 1999-2002 Apple Computer, Inc.
04fee52e
A
33 *
34 * DRI: Josh de Cesare
35 */
36
37#include <sl.h>
04fee52e
A
38
39#include "ext2fs.h"
40#include "ext2fs_dinode.h"
41#include "ext2fs_dir.h"
42
43typedef struct ext2fs_dinode Inode, *InodePtr;
44
45// Private function prototypes
46
47static long HowMany(long bufferSize, long unitSize);
48static char *ReadBlock(long blockNum, long blockOffset, long length,
49 char *buffer, long cache);
366defd1 50static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time);
04fee52e
A
51static long ResolvePathToInode(char *filePath, long *flags,
52 InodePtr fileInode, InodePtr dirInode);
53static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
873b6fa6 54 unsigned long *dirIndex, char **name);
04fee52e
A
55static long FindFileInDir(char *fileName, long *flags,
56 InodePtr fileInode, InodePtr dirInode);
57static char *ReadFileBlock(InodePtr fileInode, long blockNum, long blockOffset,
58 long length, char *buffer, long cache);
59static long ReadFile(InodePtr fileInode, long *length);
60
61
62static CICell gCurrentIH;
63static char gFSBuf[SBSIZE * 2];
64static struct m_ext2fs *gFS;
65static long gBlockSize;
366defd1 66static long gBlockSizeOld;
04fee52e
A
67static char *gTempBlock;
68static char gTempName[EXT2FS_MAXNAMLEN + 1];
69static char gTempName2[EXT2FS_MAXNAMLEN + 1];
70static Inode gRootInode;
71static Inode gFileInode;
72
73// Public functions
74
75long 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;
366defd1
A
95 if (gBlockSizeOld <= gBlockSize) {
96 gTempBlock = AllocateBootXMemory(gBlockSize);
97 }
04fee52e
A
98 CacheInit(ih, gBlockSize);
99
366defd1
A
100 gBlockSizeOld = gBlockSize;
101
04fee52e
A
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;
366defd1 117 gFS->e2fs_gd = AllocateBootXMemory(gFS->e2fs_ngdb * gFS->e2fs_bsize);
04fee52e
A
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
366defd1 128 ReadInode(EXT2_ROOTINO, &gRootInode, 0, 0);
04fee52e
A
129
130 return 0;
131}
132
133
134long 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);
366defd1
A
146 if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;
147
148 if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;
04fee52e
A
149
150 ret = ReadFile(&gFileInode, &length);
151 if (ret != 0) return -1;
152
153 return length;
154}
155
873b6fa6 156long Ext2GetDirEntry(CICell ih, char *dirPath, unsigned long *dirIndex,
04fee52e
A
157 char **name, long *flags, long *time)
158{
366defd1
A
159 long ret, fileInodeNum, dirFlags;
160 Inode tmpInode;
04fee52e
A
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);
366defd1
A
167 if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory))
168 return -1;
04fee52e 169
366defd1
A
170 ret = ReadDirEntry(&gFileInode, &fileInodeNum, dirIndex, name);
171 if (ret != 0) return ret;
04fee52e 172
366defd1
A
173 ReadInode(fileInodeNum, &tmpInode, flags, time);
174
175 return 0;
04fee52e
A
176}
177
8be739c0
A
178// XX no support in AppleFileSystemDriver yet
179long 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
04fee52e
A
188// Private functions
189
190static long HowMany(long bufferSize, long unitSize)
191{
192 return (bufferSize + unitSize - 1) / unitSize;
193}
194
195
196static 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
366defd1 216static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time)
04fee52e
A
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
366defd1
A
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
04fee52e
A
239 return 0;
240}
241
242
243static 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
366defd1 270 if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory))
04fee52e
A
271 ret = ResolvePathToInode(restPath, flags, fileInode, fileInode);
272
273 return ret;
274}
275
276
277static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
873b6fa6 278 unsigned long *dirIndex, char **name)
04fee52e
A
279{
280 struct ext2fs_direct *dir;
281 char *buffer;
366defd1 282 long offset, index;
04fee52e 283 long blockNum, inodeNum;
04fee52e 284
366defd1
A
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 }
04fee52e 302
04fee52e
A
303 *fileInodeNum = inodeNum;
304 *name = strncpy(gTempName2, dir->e2d_name, dir->e2d_namlen);
305
04fee52e
A
306 return 0;
307}
308
309
310static long FindFileInDir(char *fileName, long *flags,
311 InodePtr fileInode, InodePtr dirInode)
312{
873b6fa6
A
313 long ret, inodeNum;
314 unsigned long index = 0;
04fee52e
A
315 char *name;
316
317 while (1) {
366defd1 318 ret = ReadDirEntry(dirInode, &inodeNum, &index, &name);
04fee52e
A
319 if (ret == -1) return -1;
320
04fee52e
A
321 if (strcmp(fileName, name) == 0) break;
322 }
323
366defd1 324 ReadInode(inodeNum, fileInode, flags, 0);
04fee52e
A
325
326 return 0;
327}
328
329
330static 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)) {
8be739c0 354 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 1]);
04fee52e 355 } else {
8be739c0 356 blockNum -= refsPerBlock * refsPerBlock;
04fee52e 357
8be739c0
A
358 // Get Triple Indirect Block Number.
359 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 2]);
04fee52e 360
8be739c0
A
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]);
04fee52e
A
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
382static 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}