]>
git.saurik.com Git - apple/bootx.git/blob - bootx.tproj/fs.subproj/ext2fs.c
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * ext2.c - File System Module for Ext2.
32 * Copyright (c) 1999-2002 Apple Computer, Inc.
40 #include "ext2fs_dinode.h"
41 #include "ext2fs_dir.h"
43 typedef struct ext2fs_dinode Inode
, *InodePtr
;
45 // Private function prototypes
47 static long HowMany(long bufferSize
, long unitSize
);
48 static char *ReadBlock(long blockNum
, long blockOffset
, long length
,
49 char *buffer
, long cache
);
50 static long ReadInode(long inodeNum
, InodePtr inode
, long *flags
, long *time
);
51 static long ResolvePathToInode(char *filePath
, long *flags
,
52 InodePtr fileInode
, InodePtr dirInode
);
53 static long ReadDirEntry(InodePtr dirInode
, long *fileInodeNum
,
54 unsigned long *dirIndex
, char **name
);
55 static long FindFileInDir(char *fileName
, long *flags
,
56 InodePtr fileInode
, InodePtr dirInode
);
57 static char *ReadFileBlock(InodePtr fileInode
, long blockNum
, long blockOffset
,
58 long length
, char *buffer
, long cache
);
59 static long ReadFile(InodePtr fileInode
, long *length
);
62 static CICell gCurrentIH
;
63 static char gFSBuf
[SBSIZE
* 2];
64 static struct m_ext2fs
*gFS
;
65 static long gBlockSize
;
66 static long gBlockSizeOld
;
67 static char *gTempBlock
;
68 static char gTempName
[EXT2FS_MAXNAMLEN
+ 1];
69 static char gTempName2
[EXT2FS_MAXNAMLEN
+ 1];
70 static Inode gRootInode
;
71 static Inode gFileInode
;
75 long Ext2InitPartition(CICell ih
)
79 if (ih
== gCurrentIH
) return 0;
81 printf("Ext2InitPartition: %x\n", ih
);
85 // Read for the Super Block.
87 Read(ih
, (long)gFSBuf
, SBSIZE
);
89 gFS
= (struct m_ext2fs
*)gFSBuf
;
90 e2fs_sb_bswap(&gFS
->e2fs
, &gFS
->e2fs
);
91 if (gFS
->e2fs
.e2fs_magic
!= E2FS_MAGIC
) return -1;
93 // Calculate the block size and set up the block cache.
94 gBlockSize
= 1024 << gFS
->e2fs
.e2fs_log_bsize
;
95 if (gBlockSizeOld
<= gBlockSize
) {
96 gTempBlock
= AllocateBootXMemory(gBlockSize
);
98 CacheInit(ih
, gBlockSize
);
100 gBlockSizeOld
= gBlockSize
;
104 gdPerBlock
= gBlockSize
/ sizeof(struct ext2_gd
);
106 // Fill in the in memory super block fields.
107 gFS
->e2fs_bsize
= 1024 << gFS
->e2fs
.e2fs_log_bsize
;
108 gFS
->e2fs_bshift
= LOG_MINBSIZE
+ gFS
->e2fs
.e2fs_log_bsize
;
109 gFS
->e2fs_qbmask
= gFS
->e2fs_bsize
- 1;
110 gFS
->e2fs_bmask
= ~gFS
->e2fs_qbmask
;
111 gFS
->e2fs_fsbtodb
= gFS
->e2fs
.e2fs_log_bsize
+ 1;
112 gFS
->e2fs_ncg
= HowMany(gFS
->e2fs
.e2fs_bcount
- gFS
->e2fs
.e2fs_first_dblock
,
114 gFS
->e2fs_ngdb
= HowMany(gFS
->e2fs_ncg
, gdPerBlock
);
115 gFS
->e2fs_ipb
= gFS
->e2fs_bsize
/ EXT2_DINODE_SIZE
;
116 gFS
->e2fs_itpg
= gFS
->e2fs
.e2fs_ipg
/ gFS
->e2fs_ipb
;
117 gFS
->e2fs_gd
= AllocateBootXMemory(gFS
->e2fs_ngdb
* gFS
->e2fs_bsize
);
119 // Read the summary information from disk.
120 for (cnt
= 0; cnt
< gFS
->e2fs_ngdb
; cnt
++) {
121 ReadBlock(((gBlockSize
> 1024) ? 0 : 1) + cnt
+ 1, 0, gBlockSize
,
122 (char *)&gFS
->e2fs_gd
[gdPerBlock
* cnt
], 0);
123 e2fs_cg_bswap(&gFS
->e2fs_gd
[gdPerBlock
* cnt
],
124 &gFS
->e2fs_gd
[gdPerBlock
* cnt
], gBlockSize
);
127 // Read the Root Inode
128 ReadInode(EXT2_ROOTINO
, &gRootInode
, 0, 0);
134 long Ext2LoadFile(CICell ih
, char *filePath
)
136 long ret
, length
, flags
;
138 if (Ext2InitPartition(ih
) == -1) return -1;
140 printf("Loading Ext2 file: [%s] from %x.\n", filePath
, ih
);
142 // Skip one or two leading '\'.
143 if (*filePath
== '\\') filePath
++;
144 if (*filePath
== '\\') filePath
++;
145 ret
= ResolvePathToInode(filePath
, &flags
, &gFileInode
, &gRootInode
);
146 if ((ret
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) return -1;
148 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) return -1;
150 ret
= ReadFile(&gFileInode
, &length
);
151 if (ret
!= 0) return -1;
156 long Ext2GetDirEntry(CICell ih
, char *dirPath
, unsigned long *dirIndex
,
157 char **name
, long *flags
, long *time
)
159 long ret
, fileInodeNum
, dirFlags
;
162 if (Ext2InitPartition(ih
) == -1) return -1;
164 // Skip a leading '\' if present
165 if (dirPath
[0] == '\\') dirPath
++;
166 ret
= ResolvePathToInode(dirPath
, &dirFlags
, &gFileInode
, &gRootInode
);
167 if ((ret
== -1) || ((dirFlags
& kFileTypeMask
) != kFileTypeDirectory
))
170 ret
= ReadDirEntry(&gFileInode
, &fileInodeNum
, dirIndex
, name
);
171 if (ret
!= 0) return ret
;
173 ReadInode(fileInodeNum
, &tmpInode
, flags
, time
);
178 // XX no support in AppleFileSystemDriver yet
179 long Ext2GetUUID(CICell ih
, char *uuidStr
)
181 uint8_t *uuid
= gFS
->e2fs
.e2fs_uuid
;
183 if (Ext2InitPartition(ih
) == -1) return -1;
185 return CreateUUIDString(uuid
, sizeof(gFS
->e2fs
.e2fs_uuid
), uuidStr
);
190 static long HowMany(long bufferSize
, long unitSize
)
192 return (bufferSize
+ unitSize
- 1) / unitSize
;
196 static char *ReadBlock(long blockNum
, long blockOffset
, long length
,
197 char *buffer
, long cache
)
201 offset
= 1ULL * blockNum
* gBlockSize
;
203 if (cache
&& ((blockOffset
+ length
) <= gBlockSize
)) {
204 CacheRead(gCurrentIH
, gTempBlock
, offset
, gBlockSize
, 1);
205 if (buffer
!= 0) bcopy(gTempBlock
+ blockOffset
, buffer
, length
);
206 else buffer
= gTempBlock
+ blockOffset
;
208 offset
+= blockOffset
;
209 CacheRead(gCurrentIH
, buffer
, offset
, length
, 0);
216 static long ReadInode(long inodeNum
, InodePtr inode
, long *flags
, long *time
)
218 long blockNum
= ino_to_fsba(gFS
, inodeNum
);
219 long blockOffset
= ino_to_fsbo(gFS
, inodeNum
) * sizeof(Inode
);
221 ReadBlock(blockNum
, blockOffset
, sizeof(Inode
), (char *)inode
, 1);
222 e2fs_i_bswap(inode
, inode
);
224 if (time
!= 0) *time
= inode
->e2di_mtime
;
227 switch (inode
->e2di_mode
& EXT2_IFMT
) {
228 case EXT2_IFREG
: *flags
= kFileTypeFlat
; break;
229 case EXT2_IFDIR
: *flags
= kFileTypeDirectory
; break;
230 case EXT2_IFLNK
: *flags
= kFileTypeLink
; break;
231 default : *flags
= kFileTypeUnknown
; break;
234 *flags
|= inode
->e2di_mode
& kPermMask
;
236 if (inode
->e2di_uid
!= 0) *flags
|= kOwnerNotRoot
;
243 static long ResolvePathToInode(char *filePath
, long *flags
,
244 InodePtr fileInode
, InodePtr dirInode
)
249 // if filePath is empty the we want this directory.
250 if (*filePath
== '\0') {
251 bcopy((char *)dirInode
, (char *)fileInode
, sizeof(Inode
));
255 // Copy the file name to gTempName
257 while ((filePath
[cnt
] != '\\') && (filePath
[cnt
] != '\0')) cnt
++;
258 strncpy(gTempName
, filePath
, cnt
);
260 // Move restPath to the right place.
261 if (filePath
[cnt
] != '\0') cnt
++;
262 restPath
= filePath
+ cnt
;
264 // gTempName is a name in the current Dir.
265 // restPath is the rest of the path if any.
267 ret
= FindFileInDir(gTempName
, flags
, fileInode
, dirInode
);
268 if (ret
== -1) return -1;
270 if ((*restPath
!= '\0') && ((*flags
& kFileTypeMask
) == kFileTypeDirectory
))
271 ret
= ResolvePathToInode(restPath
, flags
, fileInode
, fileInode
);
277 static long ReadDirEntry(InodePtr dirInode
, long *fileInodeNum
,
278 unsigned long *dirIndex
, char **name
)
280 struct ext2fs_direct
*dir
;
283 long blockNum
, inodeNum
;
288 offset
= index
% gBlockSize
;
289 blockNum
= index
/ gBlockSize
;
291 buffer
= ReadFileBlock(dirInode
, blockNum
, 0, gBlockSize
, 0, 1);
292 if (buffer
== 0) return -1;
294 dir
= (struct ext2fs_direct
*)(buffer
+ offset
);
295 *dirIndex
+= bswap16(dir
->e2d_reclen
);
297 inodeNum
= bswap32(dir
->e2d_ino
);
298 if (inodeNum
!= 0) break;
300 if (offset
!= 0) return -1;
303 *fileInodeNum
= inodeNum
;
304 *name
= strncpy(gTempName2
, dir
->e2d_name
, dir
->e2d_namlen
);
310 static long FindFileInDir(char *fileName
, long *flags
,
311 InodePtr fileInode
, InodePtr dirInode
)
314 unsigned long index
= 0;
318 ret
= ReadDirEntry(dirInode
, &inodeNum
, &index
, &name
);
319 if (ret
== -1) return -1;
321 if (strcmp(fileName
, name
) == 0) break;
324 ReadInode(inodeNum
, fileInode
, flags
, 0);
330 static char *ReadFileBlock(InodePtr fileInode
, long blockNum
, long blockOffset
,
331 long length
, char *buffer
, long cache
)
333 long diskBlockNum
, indBlockNum
, indBlockOff
, refsPerBlock
;
336 if (blockNum
>= fileInode
->e2di_nblock
) return 0;
338 refsPerBlock
= gBlockSize
/ sizeof(u_int32_t
);
340 // Get Direct Block Number.
341 if (blockNum
< NDADDR
) {
342 diskBlockNum
= bswap32(fileInode
->e2di_blocks
[blockNum
]);
346 // Get Single Indirect Block Number.
347 if (blockNum
< refsPerBlock
) {
348 indBlockNum
= bswap32(fileInode
->e2di_blocks
[NDADDR
]);
350 blockNum
-= refsPerBlock
;
352 // Get Double Indirect Block Number.
353 if (blockNum
< (refsPerBlock
* refsPerBlock
)) {
354 indBlockNum
= bswap32(fileInode
->e2di_blocks
[NDADDR
+ 1]);
356 blockNum
-= refsPerBlock
* refsPerBlock
;
358 // Get Triple Indirect Block Number.
359 indBlockNum
= bswap32(fileInode
->e2di_blocks
[NDADDR
+ 2]);
361 indBlock
= ReadBlock(indBlockNum
, 0, gBlockSize
, 0, 1);
362 indBlockOff
= blockNum
/ (refsPerBlock
* refsPerBlock
);
363 blockNum
%= (refsPerBlock
* refsPerBlock
);
364 indBlockNum
= bswap32(((u_int32_t
*)indBlock
)[indBlockOff
]);
367 indBlock
= ReadBlock(indBlockNum
, 0, gBlockSize
, 0, 1);
368 indBlockOff
= blockNum
/ refsPerBlock
;
369 blockNum
%= refsPerBlock
;
370 indBlockNum
= bswap32(((u_int32_t
*)indBlock
)[indBlockOff
]);
373 indBlock
= ReadBlock(indBlockNum
, 0, gBlockSize
, 0, 1);
374 diskBlockNum
= bswap32(((u_int32_t
*)indBlock
)[blockNum
]);
377 buffer
= ReadBlock(diskBlockNum
, blockOffset
, length
, buffer
, cache
);
382 static long ReadFile(InodePtr fileInode
, long *length
)
384 long bytesLeft
, curSize
, curBlock
= 0;
385 char *buffer
, *curAddr
= (char *)kLoadAddr
;
387 bytesLeft
= *length
= fileInode
->e2di_size
;
389 if (*length
> kLoadSize
) {
390 printf("File is too large.\n");
395 if (bytesLeft
> gBlockSize
) curSize
= gBlockSize
;
396 else curSize
= bytesLeft
;
398 buffer
= ReadFileBlock(fileInode
, curBlock
, 0, curSize
, curAddr
, 0);
399 if (buffer
== 0) break;
403 bytesLeft
-= curSize
;