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