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