]>
Commit | Line | Data |
---|---|---|
04fee52e | 1 | /* |
873b6fa6 | 2 | * Copyright (c) 2000-2007 Apple Inc. All rights reserved. |
04fee52e | 3 | * |
873b6fa6 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
04fee52e | 5 | * |
873b6fa6 A |
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. | |
04fee52e | 14 | * |
873b6fa6 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
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 | |
04fee52e A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
873b6fa6 A |
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. | |
04fee52e | 25 | * |
873b6fa6 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
04fee52e | 27 | */ |
873b6fa6 | 28 | |
04fee52e A |
29 | /* |
30 | * ext2.c - File System Module for Ext2. | |
31 | * | |
366defd1 | 32 | * Copyright (c) 1999-2002 Apple Computer, Inc. |
04fee52e A |
33 | * |
34 | * DRI: Josh de Cesare | |
35 | */ | |
36 | ||
37 | #include <sl.h> | |
04fee52e A |
38 | |
39 | #include "ext2fs.h" | |
40 | #include "ext2fs_dinode.h" | |
41 | #include "ext2fs_dir.h" | |
42 | ||
43 | typedef struct ext2fs_dinode Inode, *InodePtr; | |
44 | ||
45 | // Private function prototypes | |
46 | ||
47 | static long HowMany(long bufferSize, long unitSize); | |
48 | static char *ReadBlock(long blockNum, long blockOffset, long length, | |
49 | char *buffer, long cache); | |
366defd1 | 50 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time); |
04fee52e A |
51 | static long ResolvePathToInode(char *filePath, long *flags, |
52 | InodePtr fileInode, InodePtr dirInode); | |
53 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum, | |
873b6fa6 | 54 | unsigned long *dirIndex, char **name); |
04fee52e A |
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); | |
60 | ||
61 | ||
62 | static CICell gCurrentIH; | |
63 | static char gFSBuf[SBSIZE * 2]; | |
64 | static struct m_ext2fs *gFS; | |
65 | static long gBlockSize; | |
366defd1 | 66 | static long gBlockSizeOld; |
04fee52e A |
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; | |
72 | ||
73 | // Public functions | |
74 | ||
75 | long Ext2InitPartition(CICell ih) | |
76 | { | |
77 | long cnt, gdPerBlock; | |
78 | ||
79 | if (ih == gCurrentIH) return 0; | |
80 | ||
81 | printf("Ext2InitPartition: %x\n", ih); | |
82 | ||
83 | gCurrentIH = 0; | |
84 | ||
85 | // Read for the Super Block. | |
86 | Seek(ih, SBOFF); | |
87 | Read(ih, (long)gFSBuf, SBSIZE); | |
88 | ||
89 | gFS = (struct m_ext2fs *)gFSBuf; | |
90 | e2fs_sb_bswap(&gFS->e2fs, &gFS->e2fs); | |
91 | if (gFS->e2fs.e2fs_magic != E2FS_MAGIC) return -1; | |
92 | ||
93 | // Calculate the block size and set up the block cache. | |
94 | gBlockSize = 1024 << gFS->e2fs.e2fs_log_bsize; | |
366defd1 A |
95 | if (gBlockSizeOld <= gBlockSize) { |
96 | gTempBlock = AllocateBootXMemory(gBlockSize); | |
97 | } | |
04fee52e A |
98 | CacheInit(ih, gBlockSize); |
99 | ||
366defd1 A |
100 | gBlockSizeOld = gBlockSize; |
101 | ||
04fee52e A |
102 | gCurrentIH = ih; |
103 | ||
104 | gdPerBlock = gBlockSize / sizeof(struct ext2_gd); | |
105 | ||
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, | |
113 | gFS->e2fs.e2fs_bpg); | |
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; | |
366defd1 | 117 | gFS->e2fs_gd = AllocateBootXMemory(gFS->e2fs_ngdb * gFS->e2fs_bsize); |
04fee52e A |
118 | |
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); | |
125 | } | |
126 | ||
127 | // Read the Root Inode | |
366defd1 | 128 | ReadInode(EXT2_ROOTINO, &gRootInode, 0, 0); |
04fee52e A |
129 | |
130 | return 0; | |
131 | } | |
132 | ||
133 | ||
134 | long Ext2LoadFile(CICell ih, char *filePath) | |
135 | { | |
136 | long ret, length, flags; | |
137 | ||
138 | if (Ext2InitPartition(ih) == -1) return -1; | |
139 | ||
140 | printf("Loading Ext2 file: [%s] from %x.\n", filePath, ih); | |
141 | ||
142 | // Skip one or two leading '\'. | |
143 | if (*filePath == '\\') filePath++; | |
144 | if (*filePath == '\\') filePath++; | |
145 | ret = ResolvePathToInode(filePath, &flags, &gFileInode, &gRootInode); | |
366defd1 A |
146 | if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1; |
147 | ||
148 | if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1; | |
04fee52e A |
149 | |
150 | ret = ReadFile(&gFileInode, &length); | |
151 | if (ret != 0) return -1; | |
152 | ||
153 | return length; | |
154 | } | |
155 | ||
873b6fa6 | 156 | long Ext2GetDirEntry(CICell ih, char *dirPath, unsigned long *dirIndex, |
04fee52e A |
157 | char **name, long *flags, long *time) |
158 | { | |
366defd1 A |
159 | long ret, fileInodeNum, dirFlags; |
160 | Inode tmpInode; | |
04fee52e A |
161 | |
162 | if (Ext2InitPartition(ih) == -1) return -1; | |
163 | ||
164 | // Skip a leading '\' if present | |
165 | if (dirPath[0] == '\\') dirPath++; | |
166 | ret = ResolvePathToInode(dirPath, &dirFlags, &gFileInode, &gRootInode); | |
366defd1 A |
167 | if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory)) |
168 | return -1; | |
04fee52e | 169 | |
366defd1 A |
170 | ret = ReadDirEntry(&gFileInode, &fileInodeNum, dirIndex, name); |
171 | if (ret != 0) return ret; | |
04fee52e | 172 | |
366defd1 A |
173 | ReadInode(fileInodeNum, &tmpInode, flags, time); |
174 | ||
175 | return 0; | |
04fee52e A |
176 | } |
177 | ||
8be739c0 A |
178 | // XX no support in AppleFileSystemDriver yet |
179 | long Ext2GetUUID(CICell ih, char *uuidStr) | |
180 | { | |
181 | uint8_t *uuid = gFS->e2fs.e2fs_uuid; | |
182 | ||
183 | if (Ext2InitPartition(ih) == -1) return -1; | |
184 | ||
185 | return CreateUUIDString(uuid, sizeof(gFS->e2fs.e2fs_uuid), uuidStr); | |
186 | } | |
187 | ||
04fee52e A |
188 | // Private functions |
189 | ||
190 | static long HowMany(long bufferSize, long unitSize) | |
191 | { | |
192 | return (bufferSize + unitSize - 1) / unitSize; | |
193 | } | |
194 | ||
195 | ||
196 | static char *ReadBlock(long blockNum, long blockOffset, long length, | |
197 | char *buffer, long cache) | |
198 | { | |
199 | long long offset; | |
200 | ||
201 | offset = 1ULL * blockNum * gBlockSize; | |
202 | ||
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; | |
207 | } else { | |
208 | offset += blockOffset; | |
209 | CacheRead(gCurrentIH, buffer, offset, length, 0); | |
210 | } | |
211 | ||
212 | return buffer; | |
213 | } | |
214 | ||
215 | ||
366defd1 | 216 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time) |
04fee52e A |
217 | { |
218 | long blockNum = ino_to_fsba(gFS, inodeNum); | |
219 | long blockOffset = ino_to_fsbo(gFS, inodeNum) * sizeof(Inode); | |
220 | ||
221 | ReadBlock(blockNum, blockOffset, sizeof(Inode), (char *)inode, 1); | |
222 | e2fs_i_bswap(inode, inode); | |
223 | ||
366defd1 A |
224 | if (time != 0) *time = inode->e2di_mtime; |
225 | ||
226 | if (flags != 0) { | |
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; | |
232 | } | |
233 | ||
234 | *flags |= inode->e2di_mode & kPermMask; | |
235 | ||
236 | if (inode->e2di_uid != 0) *flags |= kOwnerNotRoot; | |
237 | } | |
238 | ||
04fee52e A |
239 | return 0; |
240 | } | |
241 | ||
242 | ||
243 | static long ResolvePathToInode(char *filePath, long *flags, | |
244 | InodePtr fileInode, InodePtr dirInode) | |
245 | { | |
246 | char *restPath; | |
247 | long ret, cnt; | |
248 | ||
249 | // if filePath is empty the we want this directory. | |
250 | if (*filePath == '\0') { | |
251 | bcopy((char *)dirInode, (char *)fileInode, sizeof(Inode)); | |
252 | return 0; | |
253 | } | |
254 | ||
255 | // Copy the file name to gTempName | |
256 | cnt = 0; | |
257 | while ((filePath[cnt] != '\\') && (filePath[cnt] != '\0')) cnt++; | |
258 | strncpy(gTempName, filePath, cnt); | |
259 | ||
260 | // Move restPath to the right place. | |
261 | if (filePath[cnt] != '\0') cnt++; | |
262 | restPath = filePath + cnt; | |
263 | ||
264 | // gTempName is a name in the current Dir. | |
265 | // restPath is the rest of the path if any. | |
266 | ||
267 | ret = FindFileInDir(gTempName, flags, fileInode, dirInode); | |
268 | if (ret == -1) return -1; | |
269 | ||
366defd1 | 270 | if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory)) |
04fee52e A |
271 | ret = ResolvePathToInode(restPath, flags, fileInode, fileInode); |
272 | ||
273 | return ret; | |
274 | } | |
275 | ||
276 | ||
277 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum, | |
873b6fa6 | 278 | unsigned long *dirIndex, char **name) |
04fee52e A |
279 | { |
280 | struct ext2fs_direct *dir; | |
281 | char *buffer; | |
366defd1 | 282 | long offset, index; |
04fee52e | 283 | long blockNum, inodeNum; |
04fee52e | 284 | |
366defd1 A |
285 | while (1) { |
286 | index = *dirIndex; | |
287 | ||
288 | offset = index % gBlockSize; | |
289 | blockNum = index / gBlockSize; | |
290 | ||
291 | buffer = ReadFileBlock(dirInode, blockNum, 0, gBlockSize, 0, 1); | |
292 | if (buffer == 0) return -1; | |
293 | ||
294 | dir = (struct ext2fs_direct *)(buffer + offset); | |
295 | *dirIndex += bswap16(dir->e2d_reclen); | |
296 | ||
297 | inodeNum = bswap32(dir->e2d_ino); | |
298 | if (inodeNum != 0) break; | |
299 | ||
300 | if (offset != 0) return -1; | |
301 | } | |
04fee52e | 302 | |
04fee52e A |
303 | *fileInodeNum = inodeNum; |
304 | *name = strncpy(gTempName2, dir->e2d_name, dir->e2d_namlen); | |
305 | ||
04fee52e A |
306 | return 0; |
307 | } | |
308 | ||
309 | ||
310 | static long FindFileInDir(char *fileName, long *flags, | |
311 | InodePtr fileInode, InodePtr dirInode) | |
312 | { | |
873b6fa6 A |
313 | long ret, inodeNum; |
314 | unsigned long index = 0; | |
04fee52e A |
315 | char *name; |
316 | ||
317 | while (1) { | |
366defd1 | 318 | ret = ReadDirEntry(dirInode, &inodeNum, &index, &name); |
04fee52e A |
319 | if (ret == -1) return -1; |
320 | ||
04fee52e A |
321 | if (strcmp(fileName, name) == 0) break; |
322 | } | |
323 | ||
366defd1 | 324 | ReadInode(inodeNum, fileInode, flags, 0); |
04fee52e A |
325 | |
326 | return 0; | |
327 | } | |
328 | ||
329 | ||
330 | static char *ReadFileBlock(InodePtr fileInode, long blockNum, long blockOffset, | |
331 | long length, char *buffer, long cache) | |
332 | { | |
333 | long diskBlockNum, indBlockNum, indBlockOff, refsPerBlock; | |
334 | char *indBlock; | |
335 | ||
336 | if (blockNum >= fileInode->e2di_nblock) return 0; | |
337 | ||
338 | refsPerBlock = gBlockSize / sizeof(u_int32_t); | |
339 | ||
340 | // Get Direct Block Number. | |
341 | if (blockNum < NDADDR) { | |
342 | diskBlockNum = bswap32(fileInode->e2di_blocks[blockNum]); | |
343 | } else { | |
344 | blockNum -= NDADDR; | |
345 | ||
346 | // Get Single Indirect Block Number. | |
347 | if (blockNum < refsPerBlock) { | |
348 | indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR]); | |
349 | } else { | |
350 | blockNum -= refsPerBlock; | |
351 | ||
352 | // Get Double Indirect Block Number. | |
353 | if (blockNum < (refsPerBlock * refsPerBlock)) { | |
8be739c0 | 354 | indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 1]); |
04fee52e | 355 | } else { |
8be739c0 | 356 | blockNum -= refsPerBlock * refsPerBlock; |
04fee52e | 357 | |
8be739c0 A |
358 | // Get Triple Indirect Block Number. |
359 | indBlockNum = bswap32(fileInode->e2di_blocks[NDADDR + 2]); | |
04fee52e | 360 | |
8be739c0 A |
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]); | |
04fee52e A |
365 | } |
366 | ||
367 | indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1); | |
368 | indBlockOff = blockNum / refsPerBlock; | |
369 | blockNum %= refsPerBlock; | |
370 | indBlockNum = bswap32(((u_int32_t *)indBlock)[indBlockOff]); | |
371 | } | |
372 | ||
373 | indBlock = ReadBlock(indBlockNum, 0, gBlockSize, 0, 1); | |
374 | diskBlockNum = bswap32(((u_int32_t *)indBlock)[blockNum]); | |
375 | } | |
376 | ||
377 | buffer = ReadBlock(diskBlockNum, blockOffset, length, buffer, cache); | |
378 | ||
379 | return buffer; | |
380 | } | |
381 | ||
382 | static long ReadFile(InodePtr fileInode, long *length) | |
383 | { | |
384 | long bytesLeft, curSize, curBlock = 0; | |
385 | char *buffer, *curAddr = (char *)kLoadAddr; | |
386 | ||
387 | bytesLeft = *length = fileInode->e2di_size; | |
388 | ||
389 | if (*length > kLoadSize) { | |
390 | printf("File is too large.\n"); | |
391 | return -1; | |
392 | } | |
393 | ||
394 | while (bytesLeft) { | |
395 | if (bytesLeft > gBlockSize) curSize = gBlockSize; | |
396 | else curSize = bytesLeft; | |
397 | ||
398 | buffer = ReadFileBlock(fileInode, curBlock, 0, curSize, curAddr, 0); | |
399 | if (buffer == 0) break; | |
400 | ||
401 | curBlock++; | |
402 | curAddr += curSize; | |
403 | bytesLeft -= curSize; | |
404 | } | |
405 | ||
406 | return bytesLeft; | |
407 | } |