]>
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 | * ufs.c - File System Module for UFS. | |
31 | * | |
8be739c0 | 32 | * Copyright (c) 1998-2004 Apple Computer, Inc. |
04fee52e A |
33 | * |
34 | * DRI: Josh de Cesare | |
35 | */ | |
36 | ||
37 | #include <sl.h> | |
366defd1 | 38 | |
04fee52e A |
39 | #include "ufs_byteorder.h" |
40 | ||
95a2ba82 A |
41 | #include <dirent.h> // for MAXNAMLEN |
42 | ||
04fee52e A |
43 | typedef struct dinode Inode, *InodePtr; |
44 | ||
45 | // Private function prototypes | |
46 | ||
47 | static char *ReadBlock(long fragNum, long fragOffset, long length, | |
48 | char *buffer, long cache); | |
366defd1 | 49 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time); |
04fee52e A |
50 | static long ResolvePathToInode(char *filePath, long *flags, |
51 | InodePtr fileInode, InodePtr dirInode); | |
52 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum, | |
873b6fa6 | 53 | unsigned long *dirIndex, char **name); |
04fee52e A |
54 | static long FindFileInDir(char *fileName, long *flags, |
55 | InodePtr fileInode, InodePtr dirInode); | |
56 | static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset, | |
57 | long length, char *buffer, long cache); | |
8be739c0 A |
58 | static long ReadFile(InodePtr fileInode, long *length, |
59 | void *base, long offset); | |
04fee52e A |
60 | |
61 | ||
04fee52e A |
62 | static CICell gCurrentIH; |
63 | static long long gPartitionBase; | |
04fee52e A |
64 | static char gFSBuf[SBSIZE]; |
65 | static struct fs *gFS; | |
8be739c0 | 66 | static struct ufslabel gUFSLabel; // for UUID |
04fee52e | 67 | static long gBlockSize; |
366defd1 | 68 | static long gBlockSizeOld; |
04fee52e A |
69 | static long gFragSize; |
70 | static long gFragsPerBlock; | |
71 | static char *gTempBlock; | |
72 | static char gTempName[MAXNAMLEN + 1]; | |
73 | static char gTempName2[MAXNAMLEN + 1]; | |
74 | static Inode gRootInode; | |
75 | static Inode gFileInode; | |
76 | ||
77 | // Public functions | |
78 | ||
79 | long UFSInitPartition(CICell ih) | |
80 | { | |
8be739c0 A |
81 | int ret; |
82 | ||
04fee52e A |
83 | if (ih == gCurrentIH) return 0; |
84 | ||
85 | printf("UFSInitPartition: %x\n", ih); | |
86 | ||
87 | gCurrentIH = 0; | |
8be739c0 A |
88 | |
89 | // Assume UFS starts at the beginning of the device | |
04fee52e A |
90 | gPartitionBase = 0; |
91 | ||
8be739c0 A |
92 | // read the disk label to get the UUID |
93 | // (rumor has it that UFS headers can be either-endian on disk; hopefully | |
94 | // that isn't true for this UUID field). | |
95 | Seek(ih, gPartitionBase + UFS_LABEL_OFFSET); | |
96 | ret = Read(ih, (long)&gUFSLabel, UFS_LABEL_SIZE); | |
97 | if(ret != UFS_LABEL_SIZE) | |
98 | bzero(&gUFSLabel, UFS_LABEL_SIZE); | |
99 | ||
04fee52e A |
100 | // Look for the Super Block |
101 | Seek(ih, gPartitionBase + SBOFF); | |
102 | Read(ih, (long)gFSBuf, SBSIZE); | |
103 | ||
104 | gFS = (struct fs *)gFSBuf; | |
8be739c0 | 105 | //printf("looking for UFS magic ... \n"); |
04fee52e | 106 | if (gFS->fs_magic != FS_MAGIC) { |
71019aa0 | 107 | return -1; |
04fee52e | 108 | } |
8be739c0 | 109 | //printf("continuing w/UFS\n"); |
04fee52e A |
110 | |
111 | // Calculate the block size and set up the block cache. | |
112 | gBlockSize = gFS->fs_bsize; | |
113 | gFragSize = gFS->fs_fsize; | |
114 | gFragsPerBlock = gBlockSize / gFragSize; | |
366defd1 A |
115 | |
116 | if (gBlockSizeOld <= gBlockSize) { | |
117 | gTempBlock = AllocateBootXMemory(gBlockSize); | |
118 | } | |
119 | ||
04fee52e A |
120 | CacheInit(ih, gBlockSize); |
121 | ||
366defd1 A |
122 | gBlockSizeOld = gBlockSize; |
123 | ||
04fee52e A |
124 | gCurrentIH = ih; |
125 | ||
126 | // Read the Root Inode | |
366defd1 | 127 | ReadInode(ROOTINO, &gRootInode, 0, 0); |
04fee52e A |
128 | |
129 | return 0; | |
130 | } | |
131 | ||
132 | ||
8be739c0 A |
133 | long UFSGetUUID(CICell ih, char *uuidStr) |
134 | { | |
135 | long long uuid = gUFSLabel.ul_uuid; | |
136 | ||
137 | if (UFSInitPartition(ih) == -1) return -1; | |
138 | if (uuid == 0LL) return -1; | |
139 | ||
140 | return CreateUUIDString((uint8_t*)(&uuid), sizeof(uuid), uuidStr); | |
141 | } | |
142 | ||
04fee52e A |
143 | long UFSLoadFile(CICell ih, char *filePath) |
144 | { | |
8be739c0 A |
145 | return UFSReadFile(ih, filePath, (void *)kLoadAddr, 0, 0); |
146 | } | |
147 | ||
148 | long UFSReadFile(CICell ih, char *filePath, void *base, | |
149 | unsigned long offset, unsigned long length) | |
150 | { | |
151 | long ret, flags; | |
04fee52e A |
152 | |
153 | if (UFSInitPartition(ih) == -1) return -1; | |
154 | ||
8be739c0 A |
155 | printf("%s UFS file: [%s] from %x.\n", |
156 | (((offset == 0) && (length == 0)) ? "Loading" : "Reading"), | |
157 | filePath, ih); | |
04fee52e A |
158 | |
159 | // Skip one or two leading '\'. | |
160 | if (*filePath == '\\') filePath++; | |
161 | if (*filePath == '\\') filePath++; | |
162 | ret = ResolvePathToInode(filePath, &flags, &gFileInode, &gRootInode); | |
366defd1 A |
163 | if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1; |
164 | ||
8be739c0 A |
165 | if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) { |
166 | printf("%s: permissions incorrect\n", filePath); | |
167 | return -1; | |
168 | } | |
04fee52e | 169 | |
8be739c0 | 170 | ret = ReadFile(&gFileInode, &length, base, offset); |
04fee52e A |
171 | if (ret == -1) return -1; |
172 | ||
173 | return length; | |
174 | } | |
175 | ||
176 | ||
873b6fa6 | 177 | long UFSGetDirEntry(CICell ih, char *dirPath, unsigned long *dirIndex, |
04fee52e A |
178 | char **name, long *flags, long *time) |
179 | { | |
366defd1 A |
180 | long ret, fileInodeNum, dirFlags; |
181 | Inode tmpInode; | |
04fee52e A |
182 | |
183 | if (UFSInitPartition(ih) == -1) return -1; | |
184 | ||
185 | // Skip a leading '\' if present | |
186 | if (*dirPath == '\\') dirPath++; | |
187 | if (*dirPath == '\\') dirPath++; | |
188 | ret = ResolvePathToInode(dirPath, &dirFlags, &gFileInode, &gRootInode); | |
366defd1 A |
189 | if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory)) |
190 | return -1; | |
04fee52e | 191 | |
366defd1 A |
192 | ret = ReadDirEntry(&gFileInode, &fileInodeNum, dirIndex, name); |
193 | if (ret != 0) return ret; | |
04fee52e | 194 | |
366defd1 A |
195 | ReadInode(fileInodeNum, &tmpInode, flags, time); |
196 | ||
197 | return 0; | |
04fee52e A |
198 | } |
199 | ||
200 | // Private functions | |
201 | ||
202 | static char *ReadBlock(long fragNum, long blockOffset, long length, | |
203 | char *buffer, long cache) | |
204 | { | |
205 | long long offset; | |
206 | long blockNum; | |
207 | ||
208 | blockNum = fragNum / gFragsPerBlock; | |
209 | fragNum -= blockNum * gFragsPerBlock; | |
210 | ||
211 | blockOffset += fragNum * gFragSize; | |
212 | ||
213 | offset = gPartitionBase + 1ULL * blockNum * gBlockSize; | |
214 | ||
215 | if (cache && ((blockOffset + length) <= gBlockSize)) { | |
216 | CacheRead(gCurrentIH, gTempBlock, offset, gBlockSize, 1); | |
217 | if (buffer != 0) bcopy(gTempBlock + blockOffset, buffer, length); | |
218 | else buffer = gTempBlock + blockOffset; | |
219 | } else { | |
220 | offset += blockOffset; | |
221 | CacheRead(gCurrentIH, buffer, offset, length, 0); | |
222 | } | |
223 | ||
224 | return buffer; | |
225 | } | |
226 | ||
227 | ||
366defd1 | 228 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time) |
04fee52e A |
229 | { |
230 | long fragNum = ino_to_fsba(gFS, inodeNum); | |
231 | long blockOffset = ino_to_fsbo(gFS, inodeNum) * sizeof(Inode); | |
232 | ||
233 | ReadBlock(fragNum, blockOffset, sizeof(Inode), (char *)inode, 1); | |
234 | ||
366defd1 A |
235 | if (time != 0) *time = inode->di_mtime; |
236 | ||
237 | if (flags != 0) { | |
238 | switch (inode->di_mode & IFMT) { | |
239 | case IFREG: *flags = kFileTypeFlat; break; | |
240 | case IFDIR: *flags = kFileTypeDirectory; break; | |
241 | case IFLNK: *flags = kFileTypeLink; break; | |
242 | default : *flags = kFileTypeUnknown; break; | |
243 | } | |
244 | ||
245 | *flags |= inode->di_mode & kPermMask; | |
246 | ||
8be739c0 A |
247 | if (inode->di_uid != 0) { |
248 | static int nwarnings = 0; | |
249 | if(nwarnings++ < 25) // so we don't warn for all in an Extensions walk | |
250 | printf("non-root file owner detected: %d\n", inode->di_uid); | |
251 | *flags |= kOwnerNotRoot; | |
252 | } | |
366defd1 A |
253 | } |
254 | ||
04fee52e A |
255 | return 0; |
256 | } | |
257 | ||
258 | ||
259 | static long ResolvePathToInode(char *filePath, long *flags, | |
260 | InodePtr fileInode, InodePtr dirInode) | |
261 | { | |
262 | char *restPath; | |
263 | long ret, cnt; | |
264 | ||
265 | // if filePath is empty the we want this directory. | |
266 | if (*filePath == '\0') { | |
267 | bcopy((char *)dirInode, (char *)fileInode, sizeof(Inode)); | |
268 | return 0; | |
269 | } | |
270 | ||
271 | // Copy the file name to gTempName | |
272 | cnt = 0; | |
273 | while ((filePath[cnt] != '\\') && (filePath[cnt] != '\0')) cnt++; | |
274 | strncpy(gTempName, filePath, cnt); | |
275 | ||
276 | // Move restPath to the right place. | |
277 | if (filePath[cnt] != '\0') cnt++; | |
278 | restPath = filePath + cnt; | |
279 | ||
280 | // gTempName is a name in the current Dir. | |
281 | // restPath is the rest of the path if any. | |
282 | ||
283 | ret = FindFileInDir(gTempName, flags, fileInode, dirInode); | |
284 | if (ret == -1) return -1; | |
285 | ||
366defd1 | 286 | if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory)) |
04fee52e A |
287 | ret = ResolvePathToInode(restPath, flags, fileInode, fileInode); |
288 | ||
289 | return ret; | |
290 | } | |
291 | ||
292 | ||
293 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum, | |
873b6fa6 | 294 | unsigned long *dirIndex, char **name) |
04fee52e A |
295 | { |
296 | struct direct *dir; | |
297 | char *buffer; | |
366defd1 | 298 | long index; |
04fee52e | 299 | long dirBlockNum, dirBlockOffset; |
04fee52e | 300 | |
366defd1 A |
301 | while (1) { |
302 | index = *dirIndex; | |
303 | ||
304 | dirBlockOffset = index % DIRBLKSIZ; | |
305 | dirBlockNum = index / DIRBLKSIZ; | |
306 | ||
307 | buffer = ReadFileBlock(dirInode, dirBlockNum, 0, DIRBLKSIZ, 0, 1); | |
308 | if (buffer == 0) return -1; | |
309 | ||
310 | dir = (struct direct *)(buffer + dirBlockOffset); | |
311 | *dirIndex += dir->d_reclen; | |
312 | ||
313 | if (dir->d_ino != 0) break; | |
314 | ||
315 | if (dirBlockOffset != 0) return -1; | |
316 | } | |
04fee52e | 317 | |
04fee52e A |
318 | *fileInodeNum = dir->d_ino; |
319 | *name = strncpy(gTempName2, dir->d_name, dir->d_namlen); | |
320 | ||
04fee52e A |
321 | return 0; |
322 | } | |
323 | ||
324 | ||
325 | static long FindFileInDir(char *fileName, long *flags, | |
326 | InodePtr fileInode, InodePtr dirInode) | |
327 | { | |
873b6fa6 A |
328 | long ret, inodeNum; |
329 | unsigned long index = 0; | |
04fee52e A |
330 | char *name; |
331 | ||
332 | while (1) { | |
366defd1 | 333 | ret = ReadDirEntry(dirInode, &inodeNum, &index, &name); |
04fee52e A |
334 | if (ret == -1) return -1; |
335 | ||
04fee52e A |
336 | if (strcmp(fileName, name) == 0) break; |
337 | } | |
338 | ||
366defd1 | 339 | ReadInode(inodeNum, fileInode, flags, 0); |
04fee52e A |
340 | |
341 | return 0; | |
342 | } | |
343 | ||
344 | ||
345 | static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset, | |
346 | long length, char *buffer, long cache) | |
347 | { | |
348 | long fragCount, blockNum; | |
349 | long diskFragNum, indFragNum, indBlockOff, refsPerBlock; | |
350 | char *indBlock; | |
351 | ||
352 | fragCount = (fileInode->di_size + gFragSize - 1) / gFragSize; | |
353 | if (fragNum >= fragCount) return 0; | |
354 | ||
355 | refsPerBlock = gBlockSize / sizeof(ufs_daddr_t); | |
356 | ||
357 | blockNum = fragNum / gFragsPerBlock; | |
358 | fragNum -= blockNum * gFragsPerBlock; | |
359 | ||
360 | // Get Direct Block Number. | |
361 | if (blockNum < NDADDR) { | |
362 | diskFragNum = fileInode->di_db[blockNum]; | |
363 | } else { | |
364 | blockNum -= NDADDR; | |
365 | ||
366 | // Get Single Indirect Fragment Number. | |
367 | if (blockNum < refsPerBlock) { | |
368 | indFragNum = fileInode->di_ib[0]; | |
369 | } else { | |
370 | blockNum -= refsPerBlock; | |
371 | ||
372 | // Get Double Indirect Fragment Number. | |
373 | if (blockNum < (refsPerBlock * refsPerBlock)) { | |
374 | indFragNum = fileInode->di_ib[1]; | |
375 | } else { | |
376 | blockNum -= refsPerBlock * refsPerBlock; | |
377 | ||
378 | // Get Triple Indirect Fragment Number. | |
379 | indFragNum = fileInode->di_ib[2]; | |
380 | ||
381 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
382 | indBlockOff = blockNum / (refsPerBlock * refsPerBlock); | |
383 | blockNum %= (refsPerBlock * refsPerBlock); | |
384 | indFragNum = ((ufs_daddr_t *)indBlock)[indBlockOff]; | |
385 | } | |
386 | ||
387 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
388 | indBlockOff = blockNum / refsPerBlock; | |
389 | blockNum %= refsPerBlock; | |
390 | indFragNum = ((ufs_daddr_t *)indBlock)[indBlockOff]; | |
391 | } | |
392 | ||
393 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1); | |
394 | diskFragNum = ((ufs_daddr_t *)indBlock)[blockNum]; | |
395 | } | |
396 | ||
397 | buffer = ReadBlock(diskFragNum+fragNum, blockOffset, length, buffer, cache); | |
398 | ||
399 | return buffer; | |
400 | } | |
401 | ||
402 | ||
8be739c0 | 403 | static long ReadFile(InodePtr fileInode, long *length, void *base, long offset) |
04fee52e | 404 | { |
8be739c0 A |
405 | long bytesLeft, curSize, curFrag; |
406 | char *buffer, *curAddr = (char *)base; | |
407 | ||
408 | bytesLeft = fileInode->di_size; | |
04fee52e | 409 | |
8be739c0 A |
410 | if (offset > bytesLeft) { |
411 | printf("Offset is too large.\n"); | |
412 | return -1; | |
413 | } | |
414 | ||
415 | if ((*length == 0) || ((offset + *length) > bytesLeft)) { | |
416 | *length = bytesLeft - offset; | |
417 | } | |
04fee52e A |
418 | |
419 | if (*length > kLoadSize) { | |
420 | printf("File is too large.\n"); | |
421 | return -1; | |
422 | } | |
423 | ||
8be739c0 A |
424 | bytesLeft = *length; |
425 | curFrag = (offset / gBlockSize) * gFragsPerBlock; | |
426 | offset %= gBlockSize; | |
427 | ||
04fee52e | 428 | while (bytesLeft) { |
8be739c0 A |
429 | curSize = gBlockSize; |
430 | if (curSize > bytesLeft) curSize = bytesLeft; | |
431 | if (offset != 0) curSize -= offset; | |
04fee52e | 432 | |
8be739c0 | 433 | buffer = ReadFileBlock(fileInode, curFrag, offset, curSize, curAddr, 0); |
04fee52e A |
434 | if (buffer == 0) break; |
435 | ||
8be739c0 A |
436 | if (offset != 0) offset = 0; |
437 | ||
04fee52e A |
438 | curFrag += gFragsPerBlock; |
439 | curAddr += curSize; | |
440 | bytesLeft -= curSize; | |
441 | } | |
442 | ||
443 | return bytesLeft; | |
444 | } |