]> git.saurik.com Git - apple/bootx.git/blob - bootx.tproj/fs.subproj/ext2fs.c
c8bdc99e1042835d3e32f5c2aa6b767518b736a8
[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 * 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-2002 Apple Computer, Inc.
26 *
27 * DRI: Josh de Cesare
28 */
29
30 #include <sl.h>
31
32 #include "ext2fs.h"
33 #include "ext2fs_dinode.h"
34 #include "ext2fs_dir.h"
35
36 typedef struct ext2fs_dinode Inode, *InodePtr;
37
38 // Private function prototypes
39
40 static long HowMany(long bufferSize, long unitSize);
41 static char *ReadBlock(long blockNum, long blockOffset, long length,
42 char *buffer, long cache);
43 static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time);
44 static long ResolvePathToInode(char *filePath, long *flags,
45 InodePtr fileInode, InodePtr dirInode);
46 static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
47 long *dirIndex, char **name);
48 static long FindFileInDir(char *fileName, long *flags,
49 InodePtr fileInode, InodePtr dirInode);
50 static char *ReadFileBlock(InodePtr fileInode, long blockNum, long blockOffset,
51 long length, char *buffer, long cache);
52 static long ReadFile(InodePtr fileInode, long *length);
53
54
55 static CICell gCurrentIH;
56 static char gFSBuf[SBSIZE * 2];
57 static struct m_ext2fs *gFS;
58 static long gBlockSize;
59 static long gBlockSizeOld;
60 static char *gTempBlock;
61 static char gTempName[EXT2FS_MAXNAMLEN + 1];
62 static char gTempName2[EXT2FS_MAXNAMLEN + 1];
63 static Inode gRootInode;
64 static Inode gFileInode;
65
66 // Public functions
67
68 long 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;
88 if (gBlockSizeOld <= gBlockSize) {
89 gTempBlock = AllocateBootXMemory(gBlockSize);
90 }
91 CacheInit(ih, gBlockSize);
92
93 gBlockSizeOld = gBlockSize;
94
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;
110 gFS->e2fs_gd = AllocateBootXMemory(gFS->e2fs_ngdb * gFS->e2fs_bsize);
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
121 ReadInode(EXT2_ROOTINO, &gRootInode, 0, 0);
122
123 return 0;
124 }
125
126
127 long 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);
139 if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;
140
141 if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;
142
143 ret = ReadFile(&gFileInode, &length);
144 if (ret != 0) return -1;
145
146 return length;
147 }
148
149
150 long Ext2GetDirEntry(CICell ih, char *dirPath, long *dirIndex,
151 char **name, long *flags, long *time)
152 {
153 long ret, fileInodeNum, dirFlags;
154 Inode tmpInode;
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);
161 if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory))
162 return -1;
163
164 ret = ReadDirEntry(&gFileInode, &fileInodeNum, dirIndex, name);
165 if (ret != 0) return ret;
166
167 ReadInode(fileInodeNum, &tmpInode, flags, time);
168
169 return 0;
170 }
171
172 // XX no support in AppleFileSystemDriver yet
173 long Ext2GetUUID(CICell ih, char *uuidStr)
174 {
175 uint8_t *uuid = gFS->e2fs.e2fs_uuid;
176
177 if (Ext2InitPartition(ih) == -1) return -1;
178
179 return CreateUUIDString(uuid, sizeof(gFS->e2fs.e2fs_uuid), uuidStr);
180 }
181
182 // Private functions
183
184 static long HowMany(long bufferSize, long unitSize)
185 {
186 return (bufferSize + unitSize - 1) / unitSize;
187 }
188
189
190 static char *ReadBlock(long blockNum, long blockOffset, long length,
191 char *buffer, long cache)
192 {
193 long long offset;
194
195 offset = 1ULL * blockNum * gBlockSize;
196
197 if (cache && ((blockOffset + length) <= gBlockSize)) {
198 CacheRead(gCurrentIH, gTempBlock, offset, gBlockSize, 1);
199 if (buffer != 0) bcopy(gTempBlock + blockOffset, buffer, length);
200 else buffer = gTempBlock + blockOffset;
201 } else {
202 offset += blockOffset;
203 CacheRead(gCurrentIH, buffer, offset, length, 0);
204 }
205
206 return buffer;
207 }
208
209
210 static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time)
211 {
212 long blockNum = ino_to_fsba(gFS, inodeNum);
213 long blockOffset = ino_to_fsbo(gFS, inodeNum) * sizeof(Inode);
214
215 ReadBlock(blockNum, blockOffset, sizeof(Inode), (char *)inode, 1);
216 e2fs_i_bswap(inode, inode);
217
218 if (time != 0) *time = inode->e2di_mtime;
219
220 if (flags != 0) {
221 switch (inode->e2di_mode & EXT2_IFMT) {
222 case EXT2_IFREG: *flags = kFileTypeFlat; break;
223 case EXT2_IFDIR: *flags = kFileTypeDirectory; break;
224 case EXT2_IFLNK: *flags = kFileTypeLink; break;
225 default : *flags = kFileTypeUnknown; break;
226 }
227
228 *flags |= inode->e2di_mode & kPermMask;
229
230 if (inode->e2di_uid != 0) *flags |= kOwnerNotRoot;
231 }
232
233 return 0;
234 }
235
236
237 static long ResolvePathToInode(char *filePath, long *flags,
238 InodePtr fileInode, InodePtr dirInode)
239 {
240 char *restPath;
241 long ret, cnt;
242
243 // if filePath is empty the we want this directory.
244 if (*filePath == '\0') {
245 bcopy((char *)dirInode, (char *)fileInode, sizeof(Inode));
246 return 0;
247 }
248
249 // Copy the file name to gTempName
250 cnt = 0;
251 while ((filePath[cnt] != '\\') && (filePath[cnt] != '\0')) cnt++;
252 strncpy(gTempName, filePath, cnt);
253
254 // Move restPath to the right place.
255 if (filePath[cnt] != '\0') cnt++;
256 restPath = filePath + cnt;
257
258 // gTempName is a name in the current Dir.
259 // restPath is the rest of the path if any.
260
261 ret = FindFileInDir(gTempName, flags, fileInode, dirInode);
262 if (ret == -1) return -1;
263
264 if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory))
265 ret = ResolvePathToInode(restPath, flags, fileInode, fileInode);
266
267 return ret;
268 }
269
270
271 static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,
272 long *dirIndex, char **name)
273 {
274 struct ext2fs_direct *dir;
275 char *buffer;
276 long offset, index;
277 long blockNum, inodeNum;
278
279 while (1) {
280 index = *dirIndex;
281
282 offset = index % gBlockSize;
283 blockNum = index / gBlockSize;
284
285 buffer = ReadFileBlock(dirInode, blockNum, 0, gBlockSize, 0, 1);
286 if (buffer == 0) return -1;
287
288 dir = (struct ext2fs_direct *)(buffer + offset);
289 *dirIndex += bswap16(dir->e2d_reclen);
290
291 inodeNum = bswap32(dir->e2d_ino);
292 if (inodeNum != 0) break;
293
294 if (offset != 0) return -1;
295 }
296
297 *fileInodeNum = inodeNum;
298 *name = strncpy(gTempName2, dir->e2d_name, dir->e2d_namlen);
299
300 return 0;
301 }
302
303
304 static long FindFileInDir(char *fileName, long *flags,
305 InodePtr fileInode, InodePtr dirInode)
306 {
307 long ret, inodeNum, index = 0;
308 char *name;
309
310 while (1) {
311 ret = ReadDirEntry(dirInode, &inodeNum, &index, &name);
312 if (ret == -1) return -1;
313
314 if (strcmp(fileName, name) == 0) break;
315 }
316
317 ReadInode(inodeNum, fileInode, flags, 0);
318
319 return 0;
320 }
321
322
323 static char *ReadFileBlock(InodePtr fileInode, long blockNum, long blockOffset,
324 long length, char *buffer, long cache)
325 {
326 long diskBlockNum, indBlockNum, indBlockOff, refsPerBlock;
327 char *indBlock;
328
329 if (blockNum >= fileInode->e2di_nblock) return 0;
330
331 refsPerBlock = gBlockSize / sizeof(u_int32_t);
332
333 // Get Direct Block Number.
334 if (blockNum < NDADDR) {
335 diskBlockNum = bswap32(fileInode->e2di_blocks[blockNum]);
336 } else {
337 blockNum -= NDADDR;
338
339 // Get Single Indirect Block Number.
340 if (blockNum < refsPerBlock) {
341 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR]);
342 } else {
343 blockNum -= refsPerBlock;
344
345 // Get Double Indirect Block Number.
346 if (blockNum < (refsPerBlock * refsPerBlock)) {
347 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 1]);
348 } else {
349 blockNum -= refsPerBlock * refsPerBlock;
350
351 // Get Triple Indirect Block Number.
352 indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 2]);
353
354 indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1);
355 indBlockOff = blockNum / (refsPerBlock * refsPerBlock);
356 blockNum %= (refsPerBlock * refsPerBlock);
357 indBlockNum = bswap32(((u_int32_t *)indBlock)[indBlockOff]);
358 }
359
360 indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1);
361 indBlockOff = blockNum / refsPerBlock;
362 blockNum %= refsPerBlock;
363 indBlockNum = bswap32(((u_int32_t *)indBlock)[indBlockOff]);
364 }
365
366 indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1);
367 diskBlockNum = bswap32(((u_int32_t *)indBlock)[blockNum]);
368 }
369
370 buffer = ReadBlock(diskBlockNum, blockOffset, length, buffer, cache);
371
372 return buffer;
373 }
374
375 static long ReadFile(InodePtr fileInode, long *length)
376 {
377 long bytesLeft, curSize, curBlock = 0;
378 char *buffer, *curAddr = (char *)kLoadAddr;
379
380 bytesLeft = *length = fileInode->e2di_size;
381
382 if (*length > kLoadSize) {
383 printf("File is too large.\n");
384 return -1;
385 }
386
387 while (bytesLeft) {
388 if (bytesLeft > gBlockSize) curSize = gBlockSize;
389 else curSize = bytesLeft;
390
391 buffer = ReadFileBlock(fileInode, curBlock, 0, curSize, curAddr, 0);
392 if (buffer == 0) break;
393
394 curBlock++;
395 curAddr += curSize;
396 bytesLeft -= curSize;
397 }
398
399 return bytesLeft;
400 }