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