]>
git.saurik.com Git - apple/boot.git/blob - i386/libsaio/ufs.c
2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 * ufs.c - File System Module for UFS.
25 * Copyright (c) 1998-2002 Apple Computer, Inc.
33 #include "ufs_byteorder.h"
35 #if !defined(MAXNAMLEN) && defined(UFSMAXNAMLEN)
36 #define MAXNAMLEN UFSMAXNAMLEN
39 typedef struct dinode Inode
, *InodePtr
;
41 // Private function prototypes
43 static char *ReadBlock(long fragNum
, long fragOffset
, long length
,
44 char *buffer
, long cache
);
45 static long ReadInode(long inodeNum
, InodePtr inode
, long *flags
, long *time
);
46 static long ResolvePathToInode(char *filePath
, long *flags
,
47 InodePtr fileInode
, InodePtr dirInode
);
48 static long ReadDirEntry(InodePtr dirInode
, long *fileInodeNum
,
49 long *dirIndex
, char **name
);
50 static long FindFileInDir(char *fileName
, long *flags
,
51 InodePtr fileInode
, InodePtr dirInode
);
52 static char *ReadFileBlock(InodePtr fileInode
, long fragNum
, long blockOffset
,
53 long length
, char *buffer
, long cache
);
54 static long ReadFile(InodePtr fileInode
, unsigned long *length
, void *base
, long offset
);
56 #define kDevBlockSize (0x200) // Size of each disk block.
57 #define kDiskLabelBlock (15) // Block the DL is in.
61 static CICell gCurrentIH
;
62 static long long gPartitionBase
;
65 static struct fs
*gFS
;
67 static struct ufslabel gUFSLabel
; // for UUID
69 static long gBlockSize
;
70 static long gFragSize
;
71 static long gFragsPerBlock
;
72 static char *gTempBlock
;
73 static char *gTempName
;
74 static char *gTempName2
;
75 static InodePtr gRootInodePtr
;
76 static InodePtr gFileInodePtr
;
80 static CICell gCurrentIH
;
81 static long long gPartitionBase
;
82 static char gDLBuf
[8192];
83 static char gFSBuf
[SBSIZE
];
84 static struct fs
*gFS
;
86 static struct ufslabel gUFSLabel
; // for UUID
88 static long gBlockSize
;
89 static long gFragSize
;
90 static long gFragsPerBlock
;
91 static char *gTempBlock
;
92 static char gTempName
[MAXNAMLEN
+ 1];
93 static char gTempName2
[MAXNAMLEN
+ 1];
94 static Inode _gRootInode
;
95 static Inode _gFileInode
;
96 static InodePtr gRootInodePtr
= &_gRootInode
;
97 static InodePtr gFileInodePtr
= &_gFileInode
;
99 #endif /* !__i386__ */
103 long UFSInitPartition( CICell ih
)
109 if (ih
== gCurrentIH
) {
111 CacheInit(ih
, gBlockSize
);
117 verbose("UFSInitPartition: %x\n", ih
);
123 if (!gULBuf
) gULBuf
= (char *) malloc(UFS_LABEL_SIZE
);
124 if (!gFSBuf
) gFSBuf
= (char *) malloc(SBSIZE
);
125 if (!gTempName
) gTempName
= (char *) malloc(MAXNAMLEN
+ 1);
126 if (!gTempName2
) gTempName2
= (char *) malloc(MAXNAMLEN
+ 1);
127 if (!gRootInodePtr
) gRootInodePtr
= (InodePtr
) malloc(sizeof(Inode
));
128 if (!gFileInodePtr
) gFileInodePtr
= (InodePtr
) malloc(sizeof(Inode
));
129 if (!gULBuf
|| !gFSBuf
|| !gTempName
|| !gTempName2
||
130 !gRootInodePtr
|| !gFileInodePtr
) return -1;
133 // Assume there is no Disk Label
137 // read the disk label to get the UUID
138 // (rumor has it that UFS headers can be either-endian on disk; hopefully
139 // that isn't true for this UUID field).
140 Seek(ih
, gPartitionBase
+ UFS_LABEL_OFFSET
);
141 ret
= Read(ih
, (long)&gUFSLabel
, UFS_LABEL_SIZE
);
143 bzero(&gUFSLabel
, UFS_LABEL_SIZE
);
146 // Look for the Super Block
147 Seek(ih
, gPartitionBase
+ SBOFF
);
148 Read(ih
, (long)gFSBuf
, SBSIZE
);
150 gFS
= (struct fs
*)gFSBuf
;
151 byte_swap_superblock(gFS
);
153 if (gFS
->fs_magic
!= FS_MAGIC
) {
157 // Calculate the block size and set up the block cache.
158 gBlockSize
= gFS
->fs_bsize
;
159 gFragSize
= gFS
->fs_fsize
;
160 gFragsPerBlock
= gBlockSize
/ gFragSize
;
161 if (gTempBlock
!= 0) free(gTempBlock
);
162 gTempBlock
= malloc(gBlockSize
);
163 CacheInit(ih
, gBlockSize
);
167 // Read the Root Inode
168 ReadInode(ROOTINO
, gRootInodePtr
, 0, 0);
175 long UFSGetUUID(CICell ih
, char *uuidStr
)
177 long long uuid
= gUFSLabel
.ul_uuid
;
179 if (UFSInitPartition(ih
) == -1) return -1;
180 if (uuid
== 0LL) return -1;
182 return CreateUUIDString((uint8_t*)(&uuid
), sizeof(uuid
), uuidStr
);
187 long UFSLoadFile( CICell ih
, char * filePath
)
189 return UFSReadFile(ih
, filePath
, (void *)gFSLoadAddress
, 0, 0);
192 long UFSReadFile( CICell ih
, char * filePath
, void * base
, unsigned long offset
, unsigned long length
)
197 verbose("Loading UFS file: [%s] from %x.\n", filePath
, (unsigned)ih
);
200 if (UFSInitPartition(ih
) == -1) return -1;
202 // Skip one or two leading '/'.
203 if (*filePath
== '/') filePath
++;
204 if (*filePath
== '/') filePath
++;
206 ret
= ResolvePathToInode(filePath
, &flags
, gFileInodePtr
, gRootInodePtr
);
207 if ((ret
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) return -1;
209 ret
= ReadFile(gFileInodePtr
, &length
, base
, offset
);
210 if (ret
== -1) return -1;
217 long UFSGetDirEntry( CICell ih
, char * dirPath
, long * dirIndex
,
218 char ** name
, long * flags
, long * time
,
219 FinderInfo
* finderInfo
, long * infoValid
)
221 long ret
, fileInodeNum
, dirFlags
;
224 if (UFSInitPartition(ih
) == -1) return -1;
226 if (infoValid
) *infoValid
= 0;
228 // Skip a leading '/' if present
229 if (*dirPath
== '/') dirPath
++;
230 if (*dirPath
== '/') dirPath
++;
232 ret
= ResolvePathToInode(dirPath
, &dirFlags
, gFileInodePtr
, gRootInodePtr
);
233 if ((ret
== -1) || ((dirFlags
& kFileTypeMask
) != kFileTypeDirectory
))
236 ret
= ReadDirEntry(gFileInodePtr
, &fileInodeNum
, dirIndex
, name
);
237 if (ret
!= 0) return ret
;
239 ReadInode(fileInodeNum
, &tmpInode
, flags
, time
);
245 UFSGetDescription(CICell ih
, char *str
, long strMaxLen
)
247 if (UFSInitPartition(ih
) == -1) { return; }
251 // Look for the Disk Label
252 Seek(ih
, 1ULL * UFS_LABEL_OFFSET
);
253 Read(ih
, (long)gULBuf
, UFS_LABEL_SIZE
);
255 ul
= (struct ufslabel
*)gULBuf
;
257 unsigned char magic_bytes
[] = UFS_LABEL_MAGIC
;
259 unsigned char *p
= (unsigned char *)&ul
->ul_magic
;
261 for (i
=0; i
<sizeof(magic_bytes
); i
++, p
++) {
262 if (*p
!= magic_bytes
[i
])
265 strncpy(str
, (const char *)ul
->ul_name
, strMaxLen
);
269 UFSGetFileBlock(CICell ih
, char *filePath
, unsigned long long *firstBlock
)
273 if (UFSInitPartition(ih
) == -1) return -1;
275 // Skip one or two leading '/'.
276 if (*filePath
== '/') filePath
++;
277 if (*filePath
== '/') filePath
++;
279 ret
= ResolvePathToInode(filePath
, &flags
, gFileInodePtr
, gRootInodePtr
);
280 if ((ret
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) return -1;
282 *firstBlock
= (gPartitionBase
+ 1ULL * gFileInodePtr
->di_db
[0] * gBlockSize
) / 512ULL;
292 static char * ReadBlock( long fragNum
, long blockOffset
, long length
,
293 char * buffer
, long cache
)
298 blockNum
= fragNum
/ gFragsPerBlock
;
299 fragNum
-= blockNum
* gFragsPerBlock
;
301 blockOffset
+= fragNum
* gFragSize
;
303 offset
= gPartitionBase
+ 1ULL * blockNum
* gBlockSize
;
305 if (cache
&& ((blockOffset
+ length
) <= gBlockSize
)) {
306 CacheRead(gCurrentIH
, gTempBlock
, offset
, gBlockSize
, 1);
307 if (buffer
!= 0) bcopy(gTempBlock
+ blockOffset
, buffer
, length
);
308 else buffer
= gTempBlock
+ blockOffset
;
310 offset
+= blockOffset
;
311 CacheRead(gCurrentIH
, buffer
, offset
, length
, 0);
317 static long ReadInode( long inodeNum
, InodePtr inode
, long * flags
, long * time
)
319 long fragNum
= ino_to_fsba(gFS
, inodeNum
);
320 long blockOffset
= ino_to_fsbo(gFS
, inodeNum
) * sizeof(Inode
);
322 ReadBlock(fragNum
, blockOffset
, sizeof(Inode
), (char *)inode
, 1);
323 byte_swap_dinode_in(inode
);
325 if (time
!= 0) *time
= inode
->di_mtime
;
328 switch (inode
->di_mode
& IFMT
) {
329 case IFREG
: *flags
= kFileTypeFlat
; break;
330 case IFDIR
: *flags
= kFileTypeDirectory
; break;
331 case IFLNK
: *flags
= kFileTypeLink
; break;
332 default : *flags
= kFileTypeUnknown
; break;
335 *flags
|= inode
->di_mode
& kPermMask
;
337 if (inode
->di_uid
!= 0) *flags
|= kOwnerNotRoot
;
343 static long ResolvePathToInode( char * filePath
, long * flags
,
344 InodePtr fileInode
, InodePtr dirInode
)
349 // if filePath is empty the we want this directory.
350 if (*filePath
== '\0') {
351 bcopy((char *)dirInode
, (char *)fileInode
, sizeof(Inode
));
355 // Copy the file name to gTempName
357 while ((filePath
[cnt
] != '/') && (filePath
[cnt
] != '\0')) cnt
++;
358 strlcpy(gTempName
, filePath
, cnt
+1);
360 // Move restPath to the right place.
361 if (filePath
[cnt
] != '\0') cnt
++;
362 restPath
= filePath
+ cnt
;
364 // gTempName is a name in the current Dir.
365 // restPath is the rest of the path if any.
367 ret
= FindFileInDir(gTempName
, flags
, fileInode
, dirInode
);
368 if (ret
== -1) return -1;
370 if ((*restPath
!= '\0') && ((*flags
& kFileTypeMask
) == kFileTypeDirectory
))
371 ret
= ResolvePathToInode(restPath
, flags
, fileInode
, fileInode
);
376 static long ReadDirEntry( InodePtr dirInode
, long * fileInodeNum
,
377 long * dirIndex
, char ** name
)
382 long dirBlockNum
, dirBlockOffset
;
387 dirBlockOffset
= index
% DIRBLKSIZ
;
388 dirBlockNum
= index
/ DIRBLKSIZ
;
390 buffer
= ReadFileBlock(dirInode
, dirBlockNum
, 0, DIRBLKSIZ
, 0, 1);
391 if (buffer
== 0) return -1;
393 dir
= (struct direct
*)(buffer
+ dirBlockOffset
);
394 byte_swap_dir_block_in((char *)dir
, 1);
396 *dirIndex
+= dir
->d_reclen
;
398 if (dir
->d_ino
!= 0) break;
400 if (dirBlockOffset
!= 0) return -1;
403 *fileInodeNum
= dir
->d_ino
;
404 *name
= strlcpy(gTempName2
, dir
->d_name
, dir
->d_namlen
+1);
409 static long FindFileInDir( char * fileName
, long * flags
,
410 InodePtr fileInode
, InodePtr dirInode
)
412 long ret
, inodeNum
, index
= 0;
416 ret
= ReadDirEntry(dirInode
, &inodeNum
, &index
, &name
);
417 if (ret
== -1) return -1;
419 if (strcmp(fileName
, name
) == 0) break;
422 ReadInode(inodeNum
, fileInode
, flags
, 0);
427 static char * ReadFileBlock( InodePtr fileInode
, long fragNum
, long blockOffset
,
428 long length
, char * buffer
, long cache
)
430 long fragCount
, blockNum
;
431 long diskFragNum
, indFragNum
, indBlockOff
, refsPerBlock
;
434 fragCount
= (fileInode
->di_size
+ gFragSize
- 1) / gFragSize
;
435 if (fragNum
>= fragCount
) return 0;
437 refsPerBlock
= gBlockSize
/ sizeof(ufs_daddr_t
);
439 blockNum
= fragNum
/ gFragsPerBlock
;
440 fragNum
-= blockNum
* gFragsPerBlock
;
442 // Get Direct Block Number.
443 if (blockNum
< NDADDR
) {
444 diskFragNum
= fileInode
->di_db
[blockNum
];
448 // Get Single Indirect Fragment Number.
449 if (blockNum
< refsPerBlock
) {
450 indFragNum
= fileInode
->di_ib
[0];
452 blockNum
-= refsPerBlock
;
454 // Get Double Indirect Fragment Number.
455 if (blockNum
< (refsPerBlock
* refsPerBlock
)) {
456 indFragNum
= fileInode
->di_ib
[1];
458 blockNum
-= refsPerBlock
* refsPerBlock
;
460 // Get Triple Indirect Fragment Number.
461 indFragNum
= fileInode
->di_ib
[2];
463 indBlock
= ReadBlock(indFragNum
, 0, gBlockSize
, 0, 1);
464 indBlockOff
= blockNum
/ (refsPerBlock
* refsPerBlock
);
465 blockNum
%= (refsPerBlock
* refsPerBlock
);
466 indFragNum
= SWAP_BE32(((ufs_daddr_t
*)indBlock
)[indBlockOff
]);
469 indBlock
= ReadBlock(indFragNum
, 0, gBlockSize
, 0, 1);
470 indBlockOff
= blockNum
/ refsPerBlock
;
471 blockNum
%= refsPerBlock
;
472 indFragNum
= SWAP_BE32(((ufs_daddr_t
*)indBlock
)[indBlockOff
]);
475 indBlock
= ReadBlock(indFragNum
, 0, gBlockSize
, 0, 1);
476 diskFragNum
= SWAP_BE32(((ufs_daddr_t
*)indBlock
)[blockNum
]);
479 buffer
= ReadBlock(diskFragNum
+fragNum
, blockOffset
, length
, buffer
, cache
);
484 static long ReadFile( InodePtr fileInode
, unsigned long * length
, void * base
, long offset
)
486 long bytesLeft
, curSize
, curFrag
;
487 char *buffer
, *curAddr
= (char *)base
;
489 bytesLeft
= fileInode
->di_size
;
491 if (offset
> bytesLeft
) {
492 printf("Offset is too large.\n");
496 if ((*length
== 0) || ((offset
+ *length
) > bytesLeft
)) {
497 *length
= bytesLeft
- offset
;
500 if (bytesLeft
> kLoadSize
) {
501 printf("File is too large.\n");
506 curFrag
= (offset
/ gBlockSize
) * gFragsPerBlock
;
507 offset
%= gBlockSize
;
510 curSize
= gBlockSize
;
511 if (curSize
> bytesLeft
) curSize
= bytesLeft
;
512 if ((offset
+ curSize
) > gBlockSize
) curSize
= (gBlockSize
- offset
);
514 buffer
= ReadFileBlock(fileInode
, curFrag
, offset
, curSize
, curAddr
, 0);
515 if (buffer
== 0) break;
517 if (offset
!= 0) offset
= 0;
519 curFrag
+= gFragsPerBlock
;
521 bytesLeft
-= curSize
;