1 | /*␊ |
2 | * Copyright (c) 2000-2003 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 2.0 (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 | #include "sl.h"␊ |
30 | ␊ |
31 | #include "ufs.h"␊ |
32 | #include "ufs_byteorder.h"␊ |
33 | ␊ |
34 | #if !defined(MAXNAMLEN) && defined(UFSMAXNAMLEN)␊ |
35 | #define MAXNAMLEN UFSMAXNAMLEN␊ |
36 | #endif␊ |
37 | ␊ |
38 | typedef struct dinode Inode, *InodePtr;␊ |
39 | ␊ |
40 | // Private function prototypes␊ |
41 | ␊ |
42 | static char *ReadBlock(long fragNum, long fragOffset, long length,␊ |
43 | char *buffer, long cache);␊ |
44 | static long ReadInode(long inodeNum, InodePtr inode, long *flags, long *time);␊ |
45 | static long ResolvePathToInode(char *filePath, long *flags,␊ |
46 | InodePtr fileInode, InodePtr dirInode);␊ |
47 | static long ReadDirEntry(InodePtr dirInode, long *fileInodeNum,␊ |
48 | long long *dirIndex, char **name);␊ |
49 | static long FindFileInDir(char *fileName, long *flags,␊ |
50 | InodePtr fileInode, InodePtr dirInode);␊ |
51 | static char *ReadFileBlock(InodePtr fileInode, long fragNum, long blockOffset,␊ |
52 | long length, char *buffer, long cache);␊ |
53 | static long ReadFile(InodePtr fileInode, uint64_t *length, void *base, uint64_t offset);␊ |
54 | ␊ |
55 | #define kDevBlockSize (0x200) // Size of each disk block.␊ |
56 | #define kDiskLabelBlock (15) // Block the DL is in.␊ |
57 | ␊ |
58 | #ifdef __i386__␊ |
59 | ␊ |
60 | static CICell gCurrentIH;␊ |
61 | static long long gPartitionBase;␊ |
62 | static char *gULBuf;␊ |
63 | static char *gFSBuf;␊ |
64 | static struct fs *gFS;␊ |
65 | #if !BOOT1␊ |
66 | static struct ufslabel gUFSLabel; // for UUID␊ |
67 | #endif␊ |
68 | static long gBlockSize;␊ |
69 | static long gFragSize;␊ |
70 | static long gFragsPerBlock;␊ |
71 | static char *gTempBlock;␊ |
72 | static char *gTempName;␊ |
73 | static char *gTempName2;␊ |
74 | static InodePtr gRootInodePtr;␊ |
75 | static InodePtr gFileInodePtr;␊ |
76 | ␊ |
77 | #else /* !__i386__ */␊ |
78 | ␊ |
79 | static CICell gCurrentIH;␊ |
80 | static long long gPartitionBase;␊ |
81 | static char gDLBuf[8192];␊ |
82 | static char gFSBuf[SBSIZE];␊ |
83 | static struct fs *gFS;␊ |
84 | #if !BOOT1␊ |
85 | static struct ufslabel gUFSLabel; // for UUID␊ |
86 | #endif␊ |
87 | static long gBlockSize;␊ |
88 | static long gFragSize;␊ |
89 | static long gFragsPerBlock;␊ |
90 | static char *gTempBlock;␊ |
91 | static char gTempName[MAXNAMLEN + 1];␊ |
92 | static char gTempName2[MAXNAMLEN + 1];␊ |
93 | static Inode _gRootInode;␊ |
94 | static Inode _gFileInode;␊ |
95 | static InodePtr gRootInodePtr = &_gRootInode;␊ |
96 | static InodePtr gFileInodePtr = &_gFileInode;␊ |
97 | ␊ |
98 | #endif /* !__i386__ */␊ |
99 | ␊ |
100 | // Public functions␊ |
101 | ␊ |
102 | void UFSFree(CICell ih)␊ |
103 | {␊ |
104 | if(gCurrentIH == ih)␊ |
105 | gCurrentIH = 0;␊ |
106 | free(ih);␊ |
107 | }␊ |
108 | ␊ |
109 | long UFSInitPartition( CICell ih )␊ |
110 | {␊ |
111 | #if !BOOT1␊ |
112 | long ret;␊ |
113 | #endif␊ |
114 | ␊ |
115 | if (ih == gCurrentIH) {␊ |
116 | #ifdef __i386__␊ |
117 | CacheInit(ih, gBlockSize);␊ |
118 | #endif␊ |
119 | return 0;␊ |
120 | }␊ |
121 | ␊ |
122 | #if !BOOT1␊ |
123 | verbose("UFSInitPartition: %x\n", ih);␊ |
124 | #endif␊ |
125 | ␊ |
126 | gCurrentIH = 0;␊ |
127 | ␊ |
128 | #ifdef __i386__␊ |
129 | if (!gULBuf) gULBuf = (char *) malloc(UFS_LABEL_SIZE);␊ |
130 | if (!gFSBuf) gFSBuf = (char *) malloc(SBSIZE);␊ |
131 | if (!gTempName) gTempName = (char *) malloc(MAXNAMLEN + 1);␊ |
132 | if (!gTempName2) gTempName2 = (char *) malloc(MAXNAMLEN + 1);␊ |
133 | if (!gRootInodePtr) gRootInodePtr = (InodePtr) malloc(sizeof(Inode));␊ |
134 | if (!gFileInodePtr) gFileInodePtr = (InodePtr) malloc(sizeof(Inode));␊ |
135 | if (!gULBuf || !gFSBuf || !gTempName || !gTempName2 ||␊ |
136 | !gRootInodePtr || !gFileInodePtr) return -1;␊ |
137 | #endif␊ |
138 | ␊ |
139 | // Assume there is no Disk Label␊ |
140 | gPartitionBase = 0;␊ |
141 | ␊ |
142 | #if !BOOT1␊ |
143 | // read the disk label to get the UUID␊ |
144 | // (rumor has it that UFS headers can be either-endian on disk; hopefully␊ |
145 | // that isn't true for this UUID field).␊ |
146 | Seek(ih, gPartitionBase + UFS_LABEL_OFFSET);␊ |
147 | ret = Read(ih, (long)&gUFSLabel, UFS_LABEL_SIZE);␊ |
148 | if(ret != 0)␊ |
149 | bzero(&gUFSLabel, UFS_LABEL_SIZE);␊ |
150 | #endif /* !BOOT1 */␊ |
151 | ␊ |
152 | // Look for the Super Block␊ |
153 | Seek(ih, gPartitionBase + SBOFF);␊ |
154 | Read(ih, (long)gFSBuf, SBSIZE);␊ |
155 | ␊ |
156 | gFS = (struct fs *)gFSBuf;␊ |
157 | byte_swap_superblock(gFS);␊ |
158 | ␊ |
159 | if (gFS->fs_magic != FS_MAGIC) {␊ |
160 | return -1;␊ |
161 | }␊ |
162 | ␊ |
163 | ␉ih->modTime = gFS->fs_time;␊ |
164 | ␉␊ |
165 | // Calculate the block size and set up the block cache.␊ |
166 | gBlockSize = gFS->fs_bsize;␊ |
167 | gFragSize = gFS->fs_fsize;␊ |
168 | gFragsPerBlock = gBlockSize / gFragSize;␊ |
169 | if (gTempBlock != 0) free(gTempBlock);␊ |
170 | gTempBlock = malloc(gBlockSize);␊ |
171 | ␉if (!gTempBlock) return -1;␊ |
172 | CacheInit(ih, gBlockSize);␊ |
173 | ␊ |
174 | gCurrentIH = ih;␊ |
175 | ␊ |
176 | // Read the Root Inode␊ |
177 | ReadInode(ROOTINO, gRootInodePtr, 0, 0);␊ |
178 | ␊ |
179 | return 0;␊ |
180 | }␊ |
181 | ␊ |
182 | #if !BOOT1␊ |
183 | ␊ |
184 | long UFSGetUUID(CICell ih, char *uuidStr, long strMaxLen)␊ |
185 | {␊ |
186 | (void)strMaxLen;␊ |
187 | ␊ |
188 | long long uuid = gUFSLabel.ul_uuid;␊ |
189 | ␊ |
190 | if (UFSInitPartition(ih) == -1) return -1;␊ |
191 | if (uuid == 0LL) return -1;␊ |
192 | ␊ |
193 | return CreateUUIDString((uint8_t*)(&uuid), sizeof(uuid), uuidStr, strMaxLen);␊ |
194 | }␊ |
195 | ␊ |
196 | #endif /* !BOOT1 */␊ |
197 | ␊ |
198 | long UFSLoadFile( CICell ih, char * filePath )␊ |
199 | {␊ |
200 | ␉return UFSReadFile(ih, filePath, (void *)(uint32_t)get_env(envgFSLoadAddress), 0, 0);␊ |
201 | ␊ |
202 | }␊ |
203 | ␊ |
204 | long UFSReadFile( CICell ih, char * filePath, void * base, uint64_t offset, uint64_t length )␊ |
205 | {␊ |
206 | long ret, flags;␊ |
207 | ␊ |
208 | #if !BOOT1␊ |
209 | verbose("Loading UFS file: [%s] from %x.\n", filePath, (unsigned)ih);␊ |
210 | #endif␊ |
211 | ␊ |
212 | if (UFSInitPartition(ih) == -1) return -1;␊ |
213 | ␊ |
214 | // Skip one or two leading '/'.␊ |
215 | if (*filePath == '/') filePath++;␊ |
216 | if (*filePath == '/') filePath++;␊ |
217 | ␊ |
218 | ret = ResolvePathToInode(filePath, &flags, gFileInodePtr, gRootInodePtr);␊ |
219 | if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;␊ |
220 | ␊ |
221 | ret = ReadFile(gFileInodePtr, &length, base, offset);␊ |
222 | if (ret == -1) return -1;␊ |
223 | ␊ |
224 | return length;␊ |
225 | }␊ |
226 | ␊ |
227 | #ifndef BOOT1␊ |
228 | ␊ |
229 | long UFSGetDirEntry( CICell ih, char * dirPath, long long * dirIndex,␊ |
230 | char ** name, long * flags, long * time,␊ |
231 | FinderInfo * finderInfo, long * infoValid)␊ |
232 | {␊ |
233 | long ret, fileInodeNum, dirFlags;␊ |
234 | Inode tmpInode;␊ |
235 | ␊ |
236 | if (UFSInitPartition(ih) == -1) return -1;␊ |
237 | ␊ |
238 | if (infoValid) *infoValid = 0;␊ |
239 | ␊ |
240 | // Skip a leading '/' if present␊ |
241 | if (*dirPath == '/') dirPath++;␊ |
242 | if (*dirPath == '/') dirPath++;␊ |
243 | ␊ |
244 | ret = ResolvePathToInode(dirPath, &dirFlags, gFileInodePtr, gRootInodePtr);␊ |
245 | if ((ret == -1) || ((dirFlags & kFileTypeMask) != kFileTypeDirectory))␊ |
246 | return -1;␊ |
247 | ␊ |
248 | ret = ReadDirEntry(gFileInodePtr, &fileInodeNum, dirIndex, name);␊ |
249 | if (ret != 0) return ret;␊ |
250 | ␊ |
251 | ReadInode(fileInodeNum, &tmpInode, flags, time);␊ |
252 | ␊ |
253 | return 0;␊ |
254 | }␊ |
255 | ␊ |
256 | void␊ |
257 | UFSGetDescription(CICell ih, char *str, long strMaxLen)␊ |
258 | {␊ |
259 | if (UFSInitPartition(ih) == -1) { return; }␊ |
260 | ␊ |
261 | struct ufslabel *ul;␊ |
262 | ␊ |
263 | // Look for the Disk Label␊ |
264 | Seek(ih, 1ULL * UFS_LABEL_OFFSET);␊ |
265 | Read(ih, (long)gULBuf, UFS_LABEL_SIZE);␊ |
266 | ␊ |
267 | ul = (struct ufslabel *)gULBuf;␊ |
268 | ␊ |
269 | unsigned char magic_bytes[] = UFS_LABEL_MAGIC;␊ |
270 | int i;␊ |
271 | unsigned char *p = (unsigned char *)&ul->ul_magic;␊ |
272 | ␊ |
273 | for (i=0; i<sizeof(magic_bytes); i++, p++) {␊ |
274 | if (*p != magic_bytes[i])␊ |
275 | return;␊ |
276 | }␊ |
277 | strncpy(str, (const char *)ul->ul_name, strMaxLen);␊ |
278 | }␊ |
279 | ␊ |
280 | long␊ |
281 | UFSGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)␊ |
282 | {␊ |
283 | long ret, flags;␊ |
284 | ␊ |
285 | if (UFSInitPartition(ih) == -1) return -1;␊ |
286 | ␊ |
287 | // Skip one or two leading '/'.␊ |
288 | if (*filePath == '/') filePath++;␊ |
289 | if (*filePath == '/') filePath++;␊ |
290 | ␊ |
291 | ret = ResolvePathToInode(filePath, &flags, gFileInodePtr, gRootInodePtr);␊ |
292 | if ((ret == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;␊ |
293 | ␊ |
294 | *firstBlock = (gPartitionBase + 1ULL * gFileInodePtr->di_db[0] * gBlockSize) / 512ULL;␊ |
295 | ␊ |
296 | return 0;␊ |
297 | }␊ |
298 | ␊ |
299 | ␊ |
300 | #endif /* !BOOT1 */␊ |
301 | ␊ |
302 | // Private functions␊ |
303 | ␊ |
304 | static char * ReadBlock( long fragNum, long blockOffset, long length,␊ |
305 | char * buffer, long cache )␊ |
306 | {␊ |
307 | long long offset;␊ |
308 | long blockNum;␊ |
309 | ␊ |
310 | blockNum = fragNum / gFragsPerBlock;␊ |
311 | fragNum -= blockNum * gFragsPerBlock;␊ |
312 | ␊ |
313 | blockOffset += fragNum * gFragSize;␊ |
314 | ␊ |
315 | offset = gPartitionBase + 1ULL * blockNum * gBlockSize;␊ |
316 | ␊ |
317 | if (cache && ((blockOffset + length) <= gBlockSize)) {␊ |
318 | CacheRead(gCurrentIH, gTempBlock, offset, gBlockSize, 1);␊ |
319 | if (buffer != 0) bcopy(gTempBlock + blockOffset, buffer, length);␊ |
320 | else buffer = gTempBlock + blockOffset;␊ |
321 | } else {␊ |
322 | offset += blockOffset;␊ |
323 | CacheRead(gCurrentIH, buffer, offset, length, 0);␊ |
324 | }␊ |
325 | ␊ |
326 | return buffer;␊ |
327 | }␊ |
328 | ␊ |
329 | static long ReadInode( long inodeNum, InodePtr inode, long * flags, long * time )␊ |
330 | {␊ |
331 | long fragNum = ino_to_fsba(gFS, inodeNum);␊ |
332 | long blockOffset = ino_to_fsbo(gFS, inodeNum) * sizeof(Inode);␊ |
333 | ␊ |
334 | ReadBlock(fragNum, blockOffset, sizeof(Inode), (char *)inode, 1);␊ |
335 | byte_swap_dinode_in(inode);␊ |
336 | ␊ |
337 | if (time != 0) *time = inode->di_mtime;␊ |
338 | ␊ |
339 | if (flags != 0) {␊ |
340 | switch (inode->di_mode & IFMT) {␊ |
341 | case IFREG: *flags = kFileTypeFlat; break;␊ |
342 | case IFDIR: *flags = kFileTypeDirectory; break;␊ |
343 | case IFLNK: *flags = kFileTypeLink; break;␊ |
344 | default : *flags = kFileTypeUnknown; break;␊ |
345 | }␊ |
346 | ␊ |
347 | *flags |= inode->di_mode & kPermMask;␊ |
348 | ␊ |
349 | if (inode->di_uid != 0) *flags |= kOwnerNotRoot;␊ |
350 | }␊ |
351 | ␊ |
352 | return 0;␊ |
353 | }␊ |
354 | ␊ |
355 | static long ResolvePathToInode( char * filePath, long * flags,␊ |
356 | InodePtr fileInode, InodePtr dirInode )␊ |
357 | {␊ |
358 | char * restPath;␊ |
359 | long ret, cnt;␊ |
360 | ␊ |
361 | // if filePath is empty the we want this directory.␊ |
362 | if (*filePath == '\0') {␊ |
363 | bcopy((char *)dirInode, (char *)fileInode, sizeof(Inode));␊ |
364 | return 0;␊ |
365 | }␊ |
366 | ␊ |
367 | // Copy the file name to gTempName␊ |
368 | cnt = 0;␊ |
369 | while ((filePath[cnt] != '/') && (filePath[cnt] != '\0')) cnt++;␊ |
370 | strlcpy(gTempName, filePath, cnt+1);␊ |
371 | ␊ |
372 | // Move restPath to the right place.␊ |
373 | if (filePath[cnt] != '\0') cnt++;␊ |
374 | restPath = filePath + cnt;␊ |
375 | ␊ |
376 | // gTempName is a name in the current Dir.␊ |
377 | // restPath is the rest of the path if any.␊ |
378 | ␊ |
379 | ret = FindFileInDir(gTempName, flags, fileInode, dirInode);␊ |
380 | if (ret == -1) return -1;␊ |
381 | ␊ |
382 | if ((*restPath != '\0') && ((*flags & kFileTypeMask) == kFileTypeDirectory))␊ |
383 | ret = ResolvePathToInode(restPath, flags, fileInode, fileInode);␊ |
384 | ␊ |
385 | return ret;␊ |
386 | }␊ |
387 | ␊ |
388 | static long ReadDirEntry( InodePtr dirInode, long * fileInodeNum,␊ |
389 | long long * dirIndex, char ** name )␊ |
390 | {␊ |
391 | struct direct *dir;␊ |
392 | char *buffer;␊ |
393 | long long index;␊ |
394 | long dirBlockNum, dirBlockOffset;␊ |
395 | ␊ |
396 | while (1) {␊ |
397 | index = *dirIndex;␊ |
398 | ␊ |
399 | dirBlockOffset = (long) (index % DIRBLKSIZ);␊ |
400 | dirBlockNum = (long) (index / DIRBLKSIZ);␊ |
401 | ␊ |
402 | buffer = ReadFileBlock(dirInode, dirBlockNum, 0, DIRBLKSIZ, 0, 1);␊ |
403 | if (buffer == 0) return -1;␊ |
404 | ␊ |
405 | dir = (struct direct *)(buffer + dirBlockOffset);␊ |
406 | byte_swap_dir_block_in((char *)dir, 1);␊ |
407 | ␊ |
408 | *dirIndex += dir->d_reclen;␊ |
409 | ␊ |
410 | if (dir->d_ino != 0) break;␊ |
411 | ␊ |
412 | if (dirBlockOffset != 0) return -1;␊ |
413 | }␊ |
414 | ␊ |
415 | *fileInodeNum = dir->d_ino;␊ |
416 | *name = strlcpy(gTempName2, dir->d_name, dir->d_namlen+1);␊ |
417 | ␊ |
418 | return 0;␊ |
419 | }␊ |
420 | ␊ |
421 | static long FindFileInDir( char * fileName, long * flags,␊ |
422 | InodePtr fileInode, InodePtr dirInode )␊ |
423 | {␊ |
424 | long ret, inodeNum;␊ |
425 | long long index = 0;␊ |
426 | char *name;␊ |
427 | ␊ |
428 | while (1) {␊ |
429 | ret = ReadDirEntry(dirInode, &inodeNum, &index, &name);␊ |
430 | if (ret == -1) return -1;␊ |
431 | ␊ |
432 | if (strcmp(fileName, name) == 0) break;␊ |
433 | }␊ |
434 | ␊ |
435 | ReadInode(inodeNum, fileInode, flags, 0);␊ |
436 | ␊ |
437 | return 0;␊ |
438 | }␊ |
439 | ␊ |
440 | static char * ReadFileBlock( InodePtr fileInode, long fragNum, long blockOffset,␊ |
441 | long length, char * buffer, long cache )␊ |
442 | {␊ |
443 | long fragCount, blockNum;␊ |
444 | long diskFragNum, indFragNum, indBlockOff, refsPerBlock;␊ |
445 | char *indBlock;␊ |
446 | ␊ |
447 | fragCount = (fileInode->di_size + gFragSize - 1) / gFragSize;␊ |
448 | if (fragNum >= fragCount) return 0;␊ |
449 | ␊ |
450 | refsPerBlock = gBlockSize / sizeof(ufs_daddr_t);␊ |
451 | ␊ |
452 | blockNum = fragNum / gFragsPerBlock;␊ |
453 | fragNum -= blockNum * gFragsPerBlock;␊ |
454 | ␊ |
455 | // Get Direct Block Number.␊ |
456 | if (blockNum < NDADDR) {␊ |
457 | diskFragNum = fileInode->di_db[blockNum];␊ |
458 | } else {␊ |
459 | blockNum -= NDADDR;␊ |
460 | ␊ |
461 | // Get Single Indirect Fragment Number.␊ |
462 | if (blockNum < refsPerBlock) {␊ |
463 | indFragNum = fileInode->di_ib[0];␊ |
464 | } else {␊ |
465 | blockNum -= refsPerBlock;␊ |
466 | ␊ |
467 | // Get Double Indirect Fragment Number.␊ |
468 | if (blockNum < (refsPerBlock * refsPerBlock)) {␊ |
469 | indFragNum = fileInode->di_ib[1];␊ |
470 | } else {␊ |
471 | blockNum -= refsPerBlock * refsPerBlock;␊ |
472 | ␊ |
473 | // Get Triple Indirect Fragment Number.␊ |
474 | indFragNum = fileInode->di_ib[2];␊ |
475 | ␊ |
476 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1);␊ |
477 | indBlockOff = blockNum / (refsPerBlock * refsPerBlock);␊ |
478 | blockNum %= (refsPerBlock * refsPerBlock);␊ |
479 | indFragNum = SWAP_BE32(((ufs_daddr_t *)indBlock)[indBlockOff]);␊ |
480 | }␊ |
481 | ␊ |
482 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1);␊ |
483 | indBlockOff = blockNum / refsPerBlock;␊ |
484 | blockNum %= refsPerBlock;␊ |
485 | indFragNum = SWAP_BE32(((ufs_daddr_t *)indBlock)[indBlockOff]);␊ |
486 | }␊ |
487 | ␊ |
488 | indBlock = ReadBlock(indFragNum, 0, gBlockSize, 0, 1);␊ |
489 | diskFragNum = SWAP_BE32(((ufs_daddr_t *)indBlock)[blockNum]);␊ |
490 | }␊ |
491 | ␊ |
492 | buffer = ReadBlock(diskFragNum+fragNum, blockOffset, length, buffer, cache);␊ |
493 | ␊ |
494 | return buffer;␊ |
495 | }␊ |
496 | ␊ |
497 | static long ReadFile( InodePtr fileInode, uint64_t * length, void * base, uint64_t offset )␊ |
498 | {␊ |
499 | long bytesLeft, curSize, curFrag;␊ |
500 | char *buffer, *curAddr = (char *)base;␊ |
501 | ␊ |
502 | bytesLeft = fileInode->di_size;␊ |
503 | ␊ |
504 | if (offset > bytesLeft) {␊ |
505 | printf("Offset is too large.\n");␊ |
506 | return -1;␊ |
507 | }␊ |
508 | ␊ |
509 | if ((*length == 0) || ((offset + *length) > bytesLeft)) {␊ |
510 | *length = bytesLeft - offset;␊ |
511 | }␊ |
512 | /*␊ |
513 | if (bytesLeft > kLoadSize) {␊ |
514 | printf("File is too large.\n");␊ |
515 | return -1;␊ |
516 | }␊ |
517 | */␊ |
518 | bytesLeft = *length;␊ |
519 | curFrag = (offset / gBlockSize) * gFragsPerBlock;␊ |
520 | offset %= gBlockSize;␊ |
521 | ␊ |
522 | while (bytesLeft) {␊ |
523 | curSize = gBlockSize;␊ |
524 | if (curSize > bytesLeft) curSize = bytesLeft;␊ |
525 | if ((offset + curSize) > gBlockSize) curSize = (gBlockSize - offset);␊ |
526 | ␊ |
527 | buffer = ReadFileBlock(fileInode, curFrag, offset, curSize, curAddr, 0);␊ |
528 | if (buffer == 0) break;␊ |
529 | ␊ |
530 | if (offset != 0) offset = 0;␊ |
531 | ␊ |
532 | curFrag += gFragsPerBlock;␊ |
533 | curAddr += curSize;␊ |
534 | bytesLeft -= curSize;␊ |
535 | }␊ |
536 | ␊ |
537 | return bytesLeft;␊ |
538 | } |