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