]>
Commit | Line | Data |
---|---|---|
04fee52e A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
db839b1d | 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
04fee52e | 7 | * |
db839b1d A |
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 | |
13 | * file. | |
14 | * | |
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 | |
04fee52e A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
db839b1d A |
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. | |
04fee52e A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* | |
26 | * ufs.c - File System Module for UFS. | |
27 | * | |
366defd1 | 28 | * Copyright (c) 1998-2002 Apple Computer, Inc. |
04fee52e A |
29 | * |
30 | * DRI: Josh de Cesare | |
31 | */ | |
32 | ||
33 | #include <sl.h> | |
366defd1 | 34 | |
04fee52e A |
35 | #include "ufs_byteorder.h" |
36 | ||
37 | typedef struct dinode Inode, *InodePtr; | |
38 | ||
39 | // Private function prototypes | |
40 | ||
41 | static char *ReadBlock(long fragNum, long fragOffset, long length, | |
42 | char *buffer, long cache); | |
366defd1 | 43 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time); |
04fee52e A |
44 | static long ResolvePathToInode(char *filePath, long *flags, |
45 | InodePtr fileInode, InodePtr dirInode); | |
46 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum, | |
366defd1 | 47 | long *dirIndex, char **name); |
04fee52e A |
48 | static long FindFileInDir(char *fileName, long *flags, |
49 | InodePtr fileInode, InodePtr dirInode); | |
50 | static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset, | |
51 | long length, char *buffer, long cache); | |
52 | static long ReadFile(InodePtr fileInode, long *length); | |
53 | ||
54 | ||
04fee52e A |
55 | static CICell gCurrentIH; |
56 | static long long gPartitionBase; | |
04fee52e A |
57 | static char gFSBuf[SBSIZE]; |
58 | static struct fs *gFS; | |
59 | static long gBlockSize; | |
366defd1 | 60 | static long gBlockSizeOld; |
04fee52e A |
61 | static long gFragSize; |
62 | static long gFragsPerBlock; | |
63 | static char *gTempBlock; | |
64 | static char gTempName[MAXNAMLEN + 1]; | |
65 | static char gTempName2[MAXNAMLEN + 1]; | |
66 | static Inode gRootInode; | |
67 | static Inode gFileInode; | |
68 | ||
69 | // Public functions | |
70 | ||
71 | long UFSInitPartition(CICell ih) | |
72 | { | |
04fee52e A |
73 | if (ih == gCurrentIH) return 0; |
74 | ||
75 | printf("UFSInitPartition: %x\n", ih); | |
76 | ||
77 | gCurrentIH = 0; | |
78 | ||
79 | // Assume there is no Disk Label | |
80 | gPartitionBase = 0; | |
81 | ||
82 | // Look for the Super Block | |
83 | Seek(ih, gPartitionBase + SBOFF); | |
84 | Read(ih, (long)gFSBuf, SBSIZE); | |
85 | ||
86 | gFS = (struct fs *)gFSBuf; | |
87 | if (gFS->fs_magic != FS_MAGIC) { | |
71019aa0 | 88 | return -1; |
04fee52e A |
89 | } |
90 | ||
91 | // Calculate the block size and set up the block cache. | |
92 | gBlockSize = gFS->fs_bsize; | |
93 | gFragSize = gFS->fs_fsize; | |
94 | gFragsPerBlock = gBlockSize / gFragSize; | |
366defd1 A |
95 | |
96 | if (gBlockSizeOld <= gBlockSize) { | |
97 | gTempBlock = AllocateBootXMemory(gBlockSize); | |
98 | } | |
99 | ||
04fee52e A |
100 | CacheInit(ih, gBlockSize); |
101 | ||
366defd1 A |
102 | gBlockSizeOld = gBlockSize; |
103 | ||
04fee52e A |
104 | gCurrentIH = ih; |
105 | ||
106 | // Read the Root Inode | |
366defd1 | 107 | ReadInode(ROOTINO, &gRootInode, 0, 0); |
04fee52e A |
108 | |
109 | return 0; | |
110 | } | |
111 | ||
112 | ||
113 | long UFSLoadFile(CICell ih, char *filePath) | |
114 | { | |
115 | long ret, length, flags; | |
116 | ||
117 | if (UFSInitPartition(ih) == -1) return -1; | |
118 | ||
119 | printf("Loading UFS file: [%s] from %x.\n", filePath, ih); | |
120 | ||
121 | // Skip one or two leading '\'. | |
122 | if (*filePath == '\\') filePath++; | |
123 | if (*filePath == '\\') filePath++; | |
124 | ret = ResolvePathToInode(filePath, &flags, &gFileInode, &gRootInode); | |
366defd1 A |
125 | if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1; |
126 | ||
127 | if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1; | |
04fee52e A |
128 | |
129 | ret = ReadFile(&gFileInode, &length); | |
130 | if (ret == -1) return -1; | |
131 | ||
132 | return length; | |
133 | } | |
134 | ||
135 | ||
136 | long UFSGetDirEntry(CICell ih, char *dirPath, long *dirIndex, | |
137 | char **name, long *flags, long *time) | |
138 | { | |
366defd1 A |
139 | long ret, fileInodeNum, dirFlags; |
140 | Inode tmpInode; | |
04fee52e A |
141 | |
142 | if (UFSInitPartition(ih) == -1) return -1; | |
143 | ||
144 | // Skip a leading '\' if present | |
145 | if (*dirPath == '\\') dirPath++; | |
146 | if (*dirPath == '\\') dirPath++; | |
147 | ret = ResolvePathToInode(dirPath, &dirFlags, &gFileInode, &gRootInode); | |
366defd1 A |
148 | if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory)) |
149 | return -1; | |
04fee52e | 150 | |
366defd1 A |
151 | ret = ReadDirEntry(&gFileInode, &fileInodeNum, dirIndex, name); |
152 | if (ret != 0) return ret; | |
04fee52e | 153 | |
366defd1 A |
154 | ReadInode(fileInodeNum, &tmpInode, flags, time); |
155 | ||
156 | return 0; | |
04fee52e A |
157 | } |
158 | ||
159 | // Private functions | |
160 | ||
161 | static char *ReadBlock(long fragNum, long blockOffset, long length, | |
162 | char *buffer, long cache) | |
163 | { | |
164 | long long offset; | |
165 | long blockNum; | |
166 | ||
167 | blockNum = fragNum / gFragsPerBlock; | |
168 | fragNum -= blockNum * gFragsPerBlock; | |
169 | ||
170 | blockOffset += fragNum * gFragSize; | |
171 | ||
172 | offset = gPartitionBase + 1ULL * blockNum * gBlockSize; | |
173 | ||
174 | if (cache && ((blockOffset + length) <= gBlockSize)) { | |
175 | CacheRead(gCurrentIH, gTempBlock, offset, gBlockSize, 1); | |
176 | if (buffer != 0) bcopy(gTempBlock + blockOffset, buffer, length); | |
177 | else buffer = gTempBlock + blockOffset; | |
178 | } else { | |
179 | offset += blockOffset; | |
180 | CacheRead(gCurrentIH, buffer, offset, length, 0); | |
181 | } | |
182 | ||
183 | return buffer; | |
184 | } | |
185 | ||
186 | ||
366defd1 | 187 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time) |
04fee52e A |
188 | { |
189 | long fragNum = ino_to_fsba(gFS, inodeNum); | |
190 | long blockOffset = ino_to_fsbo(gFS, inodeNum) * sizeof(Inode); | |
191 | ||
192 | ReadBlock(fragNum, blockOffset, sizeof(Inode), (char *)inode, 1); | |
193 | ||
366defd1 A |
194 | if (time != 0) *time = inode->di_mtime; |
195 | ||
196 | if (flags != 0) { | |
197 | switch (inode->di_mode & IFMT) { | |
198 | case IFREG: *flags = kFileTypeFlat; break; | |
199 | case IFDIR: *flags = kFileTypeDirectory; break; | |
200 | case IFLNK: *flags = kFileTypeLink; break; | |
201 | default : *flags = kFileTypeUnknown; break; | |
202 | } | |
203 | ||
204 | *flags |= inode->di_mode & kPermMask; | |
205 | ||
206 | if (inode->di_uid != 0) *flags |= kOwnerNotRoot; | |
207 | } | |
208 | ||
04fee52e A |
209 | return 0; |
210 | } | |
211 | ||
212 | ||
213 | static long ResolvePathToInode(char *filePath, long *flags, | |
214 | InodePtr fileInode, InodePtr dirInode) | |
215 | { | |
216 | char *restPath; | |
217 | long ret, cnt; | |
218 | ||
219 | // if filePath is empty the we want this directory. | |
220 | if (*filePath == '\0') { | |
221 | bcopy((char *)dirInode, (char *)fileInode, sizeof(Inode)); | |
222 | return 0; | |
223 | } | |
224 | ||
225 | // Copy the file name to gTempName | |
226 | cnt = 0; | |
227 | while ((filePath[cnt] != '\\') && (filePath[cnt] != '\0')) cnt++; | |
228 | strncpy(gTempName, filePath, cnt); | |
229 | ||
230 | // Move restPath to the right place. | |
231 | if (filePath[cnt] != '\0') cnt++; | |
232 | restPath = filePath + cnt; | |
233 | ||
234 | // gTempName is a name in the current Dir. | |
235 | // restPath is the rest of the path if any. | |
236 | ||
237 | ret = FindFileInDir(gTempName, flags, fileInode, dirInode); | |
238 | if (ret == -1) return -1; | |
239 | ||
366defd1 | 240 | if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory)) |
04fee52e A |
241 | ret = ResolvePathToInode(restPath, flags, fileInode, fileInode); |
242 | ||
243 | return ret; | |
244 | } | |
245 | ||
246 | ||
247 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum, | |
366defd1 | 248 | long *dirIndex, char **name) |
04fee52e A |
249 | { |
250 | struct direct *dir; | |
251 | char *buffer; | |
366defd1 | 252 | long index; |
04fee52e | 253 | long dirBlockNum, dirBlockOffset; |
04fee52e | 254 | |
366defd1 A |
255 | while (1) { |
256 | index = *dirIndex; | |
257 | ||
258 | dirBlockOffset = index % DIRBLKSIZ; | |
259 | dirBlockNum = index / DIRBLKSIZ; | |
260 | ||
261 | buffer = ReadFileBlock(dirInode, dirBlockNum, 0, DIRBLKSIZ, 0, 1); | |
262 | if (buffer == 0) return -1; | |
263 | ||
264 | dir = (struct direct *)(buffer + dirBlockOffset); | |
265 | *dirIndex += dir->d_reclen; | |
266 | ||
267 | if (dir->d_ino != 0) break; | |
268 | ||
269 | if (dirBlockOffset != 0) return -1; | |
270 | } | |
04fee52e | 271 | |
04fee52e A |
272 | *fileInodeNum = dir->d_ino; |
273 | *name = strncpy(gTempName2, dir->d_name, dir->d_namlen); | |
274 | ||
04fee52e A |
275 | return 0; |
276 | } | |
277 | ||
278 | ||
279 | static long FindFileInDir(char *fileName, long *flags, | |
280 | InodePtr fileInode, InodePtr dirInode) | |
281 | { | |
366defd1 | 282 | long ret, inodeNum, index = 0; |
04fee52e A |
283 | char *name; |
284 | ||
285 | while (1) { | |
366defd1 | 286 | ret = ReadDirEntry(dirInode, &inodeNum, &index, &name); |
04fee52e A |
287 | if (ret == -1) return -1; |
288 | ||
04fee52e A |
289 | if (strcmp(fileName, name) == 0) break; |
290 | } | |
291 | ||
366defd1 | 292 | ReadInode(inodeNum, fileInode, flags, 0); |
04fee52e A |
293 | |
294 | return 0; | |
295 | } | |
296 | ||
297 | ||
298 | static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset, | |
299 | long length, char *buffer, long cache) | |
300 | { | |
301 | long fragCount, blockNum; | |
302 | long diskFragNum, indFragNum, indBlockOff, refsPerBlock; | |
303 | char *indBlock; | |
304 | ||
305 | fragCount = (fileInode->di_size + gFragSize - 1) / gFragSize; | |
306 | if (fragNum >= fragCount) return 0; | |
307 | ||
308 | refsPerBlock = gBlockSize / sizeof(ufs_daddr_t); | |
309 | ||
310 | blockNum = fragNum / gFragsPerBlock; | |
311 | fragNum -= blockNum * gFragsPerBlock; | |
312 | ||
313 | // Get Direct Block Number. | |
314 | if (blockNum < NDADDR) { | |
315 | diskFragNum = fileInode->di_db[blockNum]; | |
316 | } else { | |
317 | blockNum -= NDADDR; | |
318 | ||
319 | // Get Single Indirect Fragment Number. | |
320 | if (blockNum < refsPerBlock) { | |
321 | indFragNum = fileInode->di_ib[0]; | |
322 | } else { | |
323 | blockNum -= refsPerBlock; | |
324 | ||
325 | // Get Double Indirect Fragment Number. | |
326 | if (blockNum < (refsPerBlock * refsPerBlock)) { | |
327 | indFragNum = fileInode->di_ib[1]; | |
328 | } else { | |
329 | blockNum -= refsPerBlock * refsPerBlock; | |
330 | ||
331 | // Get Triple Indirect Fragment Number. | |
332 | indFragNum = fileInode->di_ib[2]; | |
333 | ||
334 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
335 | indBlockOff = blockNum / (refsPerBlock * refsPerBlock); | |
336 | blockNum %= (refsPerBlock * refsPerBlock); | |
337 | indFragNum = ((ufs_daddr_t *)indBlock)[indBlockOff]; | |
338 | } | |
339 | ||
340 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
341 | indBlockOff = blockNum / refsPerBlock; | |
342 | blockNum %= refsPerBlock; | |
343 | indFragNum = ((ufs_daddr_t *)indBlock)[indBlockOff]; | |
344 | } | |
345 | ||
346 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
347 | diskFragNum = ((ufs_daddr_t *)indBlock)[blockNum]; | |
348 | } | |
349 | ||
350 | buffer = ReadBlock(diskFragNum+fragNum, blockOffset, length, buffer, cache); | |
351 | ||
352 | return buffer; | |
353 | } | |
354 | ||
355 | ||
356 | static long ReadFile(InodePtr fileInode, long *length) | |
357 | { | |
358 | long bytesLeft, curSize, curFrag = 0; | |
359 | char *buffer, *curAddr = (char *)kLoadAddr; | |
360 | ||
361 | bytesLeft = *length = fileInode->di_size; | |
362 | ||
363 | if (*length > kLoadSize) { | |
364 | printf("File is too large.\n"); | |
365 | return -1; | |
366 | } | |
367 | ||
368 | while (bytesLeft) { | |
369 | if (bytesLeft > gBlockSize) curSize = gBlockSize; | |
370 | else curSize = bytesLeft; | |
371 | ||
372 | buffer = ReadFileBlock(fileInode, curFrag, 0, curSize, curAddr, 0); | |
373 | if (buffer == 0) break; | |
374 | ||
375 | curFrag += gFragsPerBlock; | |
376 | curAddr += curSize; | |
377 | bytesLeft -= curSize; | |
378 | } | |
379 | ||
380 | return bytesLeft; | |
381 | } |