]>
git.saurik.com Git - apple/bootx.git/blob - bootx.tproj/fs.subproj/ext2fs.c
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
26 * ext2.c - File System Module for Ext2.
28 * Copyright (c) 1999-2002 Apple Computer, Inc.
36 #include "ext2fs_dinode.h"
37 #include "ext2fs_dir.h"
39 typedef struct ext2fs_dinode Inode
, *InodePtr
;
41 // Private function prototypes
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
);
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
;
71 long Ext2InitPartition(CICell ih
)
75 if (ih
== gCurrentIH
) return 0;
77 printf("Ext2InitPartition: %x\n", ih
);
81 // Read for the Super Block.
83 Read(ih
, (long)gFSBuf
, SBSIZE
);
85 gFS
= (struct m_ext2fs
*)gFSBuf
;
86 e2fs_sb_bswap(&gFS
->e2fs
, &gFS
->e2fs
);
87 if (gFS
->e2fs
.e2fs_magic
!= E2FS_MAGIC
) return -1;
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
);
94 CacheInit(ih
, gBlockSize
);
96 gBlockSizeOld
= gBlockSize
;
100 gdPerBlock
= gBlockSize
/ sizeof(struct ext2_gd
);
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
,
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
);
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
);
123 // Read the Root Inode
124 ReadInode(EXT2_ROOTINO
, &gRootInode
, 0, 0);
130 long Ext2LoadFile(CICell ih
, char *filePath
)
132 long ret
, length
, flags
;
134 if (Ext2InitPartition(ih
) == -1) return -1;
136 printf("Loading Ext2 file: [%s] from %x.\n", filePath
, ih
);
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;
144 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) return -1;
146 ret
= ReadFile(&gFileInode
, &length
);
147 if (ret
!= 0) return -1;
153 long Ext2GetDirEntry(CICell ih
, char *dirPath
, long *dirIndex
,
154 char **name
, long *flags
, long *time
)
156 long ret
, fileInodeNum
, dirFlags
;
159 if (Ext2InitPartition(ih
) == -1) return -1;
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
))
167 ret
= ReadDirEntry(&gFileInode
, &fileInodeNum
, dirIndex
, name
);
168 if (ret
!= 0) return ret
;
170 ReadInode(fileInodeNum
, &tmpInode
, flags
, time
);
177 static long HowMany(long bufferSize
, long unitSize
)
179 return (bufferSize
+ unitSize
- 1) / unitSize
;
183 static char *ReadBlock(long blockNum
, long blockOffset
, long length
,
184 char *buffer
, long cache
)
188 offset
= 1ULL * blockNum
* gBlockSize
;
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
;
195 offset
+= blockOffset
;
196 CacheRead(gCurrentIH
, buffer
, offset
, length
, 0);
203 static long ReadInode(long inodeNum
, InodePtr inode
, long *flags
, long *time
)
205 long blockNum
= ino_to_fsba(gFS
, inodeNum
);
206 long blockOffset
= ino_to_fsbo(gFS
, inodeNum
) * sizeof(Inode
);
208 ReadBlock(blockNum
, blockOffset
, sizeof(Inode
), (char *)inode
, 1);
209 e2fs_i_bswap(inode
, inode
);
211 if (time
!= 0) *time
= inode
->e2di_mtime
;
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;
221 *flags
|= inode
->e2di_mode
& kPermMask
;
223 if (inode
->e2di_uid
!= 0) *flags
|= kOwnerNotRoot
;
230 static long ResolvePathToInode(char *filePath
, long *flags
,
231 InodePtr fileInode
, InodePtr dirInode
)
236 // if filePath is empty the we want this directory.
237 if (*filePath
== '\0') {
238 bcopy((char *)dirInode
, (char *)fileInode
, sizeof(Inode
));
242 // Copy the file name to gTempName
244 while ((filePath
[cnt
] != '\\') && (filePath
[cnt
] != '\0')) cnt
++;
245 strncpy(gTempName
, filePath
, cnt
);
247 // Move restPath to the right place.
248 if (filePath
[cnt
] != '\0') cnt
++;
249 restPath
= filePath
+ cnt
;
251 // gTempName is a name in the current Dir.
252 // restPath is the rest of the path if any.
254 ret
= FindFileInDir(gTempName
, flags
, fileInode
, dirInode
);
255 if (ret
== -1) return -1;
257 if ((*restPath
!= '\0') && ((*flags
& kFileTypeMask
) == kFileTypeDirectory
))
258 ret
= ResolvePathToInode(restPath
, flags
, fileInode
, fileInode
);
264 static long ReadDirEntry(InodePtr dirInode
, long *fileInodeNum
,
265 long *dirIndex
, char **name
)
267 struct ext2fs_direct
*dir
;
270 long blockNum
, inodeNum
;
275 offset
= index
% gBlockSize
;
276 blockNum
= index
/ gBlockSize
;
278 buffer
= ReadFileBlock(dirInode
, blockNum
, 0, gBlockSize
, 0, 1);
279 if (buffer
== 0) return -1;
281 dir
= (struct ext2fs_direct
*)(buffer
+ offset
);
282 *dirIndex
+= bswap16(dir
->e2d_reclen
);
284 inodeNum
= bswap32(dir
->e2d_ino
);
285 if (inodeNum
!= 0) break;
287 if (offset
!= 0) return -1;
290 *fileInodeNum
= inodeNum
;
291 *name
= strncpy(gTempName2
, dir
->e2d_name
, dir
->e2d_namlen
);
297 static long FindFileInDir(char *fileName
, long *flags
,
298 InodePtr fileInode
, InodePtr dirInode
)
300 long ret
, inodeNum
, index
= 0;
304 ret
= ReadDirEntry(dirInode
, &inodeNum
, &index
, &name
);
305 if (ret
== -1) return -1;
307 if (strcmp(fileName
, name
) == 0) break;
310 ReadInode(inodeNum
, fileInode
, flags
, 0);
316 static char *ReadFileBlock(InodePtr fileInode
, long blockNum
, long blockOffset
,
317 long length
, char *buffer
, long cache
)
319 long diskBlockNum
, indBlockNum
, indBlockOff
, refsPerBlock
;
322 if (blockNum
>= fileInode
->e2di_nblock
) return 0;
324 refsPerBlock
= gBlockSize
/ sizeof(u_int32_t
);
326 // Get Direct Block Number.
327 if (blockNum
< NDADDR
) {
328 diskBlockNum
= bswap32(fileInode
->e2di_blocks
[blockNum
]);
332 // Get Single Indirect Block Number.
333 if (blockNum
< refsPerBlock
) {
334 indBlockNum
= bswap32(fileInode
->e2di_blocks
[NDADDR
]);
336 blockNum
-= refsPerBlock
;
338 // Get Double Indirect Block Number.
339 if (blockNum
< (refsPerBlock
* refsPerBlock
)) {
340 indBlockNum
= fileInode
->e2di_blocks
[NDADDR
+ 1];
342 blockNum
-= refsPerBlock
* refsPerBlock
;
344 // Get Triple Indirect Block Number.
345 indBlockNum
= fileInode
->e2di_blocks
[NDADDR
+ 2];
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
]);
353 indBlock
= ReadBlock(indBlockNum
, 0, gBlockSize
, 0, 1);
354 indBlockOff
= blockNum
/ refsPerBlock
;
355 blockNum
%= refsPerBlock
;
356 indBlockNum
= bswap32(((u_int32_t
*)indBlock
)[indBlockOff
]);
359 indBlock
= ReadBlock(indBlockNum
, 0, gBlockSize
, 0, 1);
360 diskBlockNum
= bswap32(((u_int32_t
*)indBlock
)[blockNum
]);
363 buffer
= ReadBlock(diskBlockNum
, blockOffset
, length
, buffer
, cache
);
368 static long ReadFile(InodePtr fileInode
, long *length
)
370 long bytesLeft
, curSize
, curBlock
= 0;
371 char *buffer
, *curAddr
= (char *)kLoadAddr
;
373 bytesLeft
= *length
= fileInode
->e2di_size
;
375 if (*length
> kLoadSize
) {
376 printf("File is too large.\n");
381 if (bytesLeft
> gBlockSize
) curSize
= gBlockSize
;
382 else curSize
= bytesLeft
;
384 buffer
= ReadFileBlock(fileInode
, curBlock
, 0, curSize
, curAddr
, 0);
385 if (buffer
== 0) break;
389 bytesLeft
-= curSize
;