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