2 * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv@gmail.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include <sys/dirent.h>
32 * Read the super block and check magic fields based on
36 do_checksb(struct ufs_super_block
*sb
, struct disk
*disk
,
37 const uint32_t sblock_off
, const uint32_t ufs_smagic
)
40 static uint32_t count
;
42 /* How many sectors are needed to fill sb struct */
44 count
= sizeof *sb
>> disk
->sector_shift
;
45 /* Get lba address based on sector size of disk */
46 lba
= sblock_off
>> (disk
->sector_shift
);
47 /* Read super block */
48 disk
->rdwr_sectors(disk
, sb
, lba
, count
, 0);
50 if (sb
->magic
== ufs_smagic
)
57 * Go through all possible ufs superblock offsets.
58 * TODO: Add UFS support to removable media (sb offset: 0).
61 ufs_checksb(struct ufs_super_block
*sb
, struct disk
*disk
)
63 /* Check for UFS1 sb */
64 if (do_checksb(sb
, disk
, UFS1_SBLOCK_OFFSET
, UFS1_SUPER_MAGIC
))
66 /* Check for UFS2 sb */
67 if (do_checksb(sb
, disk
, UFS2_SBLOCK_OFFSET
, UFS2_SUPER_MAGIC
))
69 /* UFS2 may also exist in 256k-, but this isn't the default */
70 if (do_checksb(sb
, disk
, UFS2_SBLOCK2_OFFSET
, UFS2_SUPER_MAGIC
))
77 * lblock stands for linear block address,
78 * whereas pblock is the actual blk ptr to get data from.
80 * UFS1/2 use frag addrs rather than blk ones, then
81 * the offset into the block must be calculated.
84 ufs_get_cache(struct inode
*inode
, block_t lblock
)
87 struct fs_info
*fs
= inode
->fs
;
88 struct ufs_sb_info
*sb
= UFS_SB(inode
->fs
);
89 uint64_t frag_addr
, frag_offset
;
93 frag_addr
= ufs_bmap(inode
, lblock
, NULL
);
97 frag_shift
= fs
->block_shift
- sb
->c_blk_frag_shift
;
98 /* Get fragment byte address */
99 frag_offset
= frag_addr
<< frag_shift
;
100 /* Convert frag addr to blk addr */
101 pblock
= frag_to_blk(fs
, frag_addr
);
103 data
= get_cache(fs
->fs_dev
, pblock
);
105 /* Return offset into block */
106 return data
+ (frag_offset
& (fs
->block_size
- 1));
110 * Based on fs/ext2/ext2.c
111 * find a dir entry, return it if found, or return NULL.
113 static const struct ufs_dir_entry
*
114 ufs_find_entry(struct fs_info
*fs
, struct inode
*inode
, const char *dname
)
116 const struct ufs_dir_entry
*dir
;
118 int32_t i
, offset
, maxoffset
;
121 ufs_debug("ufs_find_entry: dname: %s ", dname
);
122 for (i
= 0; i
< inode
->size
; i
+= fs
->block_size
) {
123 data
= ufs_get_cache(inode
, index
++);
125 maxoffset
= min(inode
->size
-i
, fs
->block_size
);
127 /* The smallest possible size is 9 bytes */
128 while (offset
< maxoffset
-8) {
129 dir
= (const struct ufs_dir_entry
*)(data
+ offset
);
130 if (dir
->dir_entry_len
> maxoffset
- offset
)
134 * Name fields are variable-length and null terminated,
135 * then it's possible to use strcmp directly.
137 if (dir
->inode_value
&& !strcmp(dname
, (const char *)dir
->name
)) {
138 ufs_debug("(found)\n");
141 offset
+= dir
->dir_entry_len
;
144 ufs_debug("(not found)\n");
149 * Get either UFS1/2 inode structures.
152 ufs_get_inode(struct fs_info
*fs
, int inr
)
155 uint32_t group
, inode_offset
, inode_table
;
156 uint32_t block_num
, block_off
;
158 /* Get cylinder group nr. */
159 group
= inr
/ UFS_SB(fs
)->inodes_per_cg
;
161 * Ensuring group will not exceed the range 0:groups_count-1.
162 * By the way, this should *never* happen.
163 * Unless the (on-disk) fs structure is corrupted!
165 if (group
>= UFS_SB(fs
)->groups_count
) {
166 printf("ufs_get_inode: "
167 "group(%d) exceeded the avail. range (0:%d)\n",
168 group
, UFS_SB(fs
)->groups_count
- 1);
172 /* Offset into inode table of the cylinder group */
173 inode_offset
= inr
% UFS_SB(fs
)->inodes_per_cg
;
174 /* Get inode table blk addr respective to cylinder group */
175 inode_table
= (group
* UFS_SB(fs
)->blocks_per_cg
) +
176 UFS_SB(fs
)->off_inode_tbl
;
177 /* Calculating staggering offset (UFS1 only!) */
178 if (UFS_SB(fs
)->fs_type
== UFS1
)
179 inode_table
+= UFS_SB(fs
)->ufs1
.delta_value
*
180 (group
& UFS_SB(fs
)->ufs1
.cycle_mask
);
182 /* Get blk nr and offset into the blk */
183 block_num
= inode_table
+ inode_offset
/ UFS_SB(fs
)->inodes_per_block
;
184 block_off
= inode_offset
% UFS_SB(fs
)->inodes_per_block
;
187 * Read the blk from the blk addr previously computed;
188 * Calc the inode struct offset into the read block.
190 data
= get_cache(fs
->fs_dev
, block_num
);
191 return data
+ block_off
* UFS_SB(fs
)->inode_size
;
194 static struct inode
*
195 ufs1_iget_by_inr(struct fs_info
*fs
, uint32_t inr
)
197 const struct ufs1_inode
*ufs_inode
;
203 ufs_inode
= (struct ufs1_inode
*) ufs_get_inode(fs
, inr
);
207 if (!(inode
= alloc_inode(fs
, inr
, sizeof(struct ufs_inode_pvt
))))
210 /* UFS1 doesn't support neither creation nor deletion times */
211 inode
->refcnt
= ufs_inode
->link_count
;
212 inode
->mode
= IFTODT(ufs_inode
->file_mode
);
213 inode
->size
= ufs_inode
->size
;
214 inode
->atime
= ufs_inode
->a_time
;
215 inode
->mtime
= ufs_inode
->m_time
;
216 inode
->blocks
= ufs_inode
->blocks_held
;
217 inode
->flags
= ufs_inode
->flags
;
220 * Copy and extend blk pointers to 64 bits, so avoid
221 * having two structures for inode private.
223 dest
= (uint64_t *) inode
->pvt
;
224 source
= (uint32_t *) ufs_inode
->direct_blk_ptr
;
225 for (i
= 0; i
< UFS_NBLOCKS
; i
++)
226 dest
[i
] = ((uint64_t) source
[i
]) & 0xFFFFFFFF;
231 static struct inode
*
232 ufs2_iget_by_inr(struct fs_info
*fs
, uint32_t inr
)
234 const struct ufs2_inode
*ufs_inode
;
237 ufs_inode
= (struct ufs2_inode
*) ufs_get_inode(fs
, inr
);
241 if (!(inode
= alloc_inode(fs
, inr
, sizeof(struct ufs_inode_pvt
))))
244 /* UFS2 doesn't support deletion time */
245 inode
->refcnt
= ufs_inode
->link_count
;
246 inode
->mode
= IFTODT(ufs_inode
->file_mode
);
247 inode
->size
= ufs_inode
->size
;
248 inode
->atime
= ufs_inode
->a_time
;
249 inode
->ctime
= ufs_inode
->creat_time
;
250 inode
->mtime
= ufs_inode
->m_time
;
251 inode
->blocks
= ufs_inode
->bytes_held
>> fs
->block_shift
;
252 inode
->flags
= ufs_inode
->flags
;
253 memcpy(inode
->pvt
, ufs_inode
->direct_blk_ptr
,
254 sizeof(uint64_t) * UFS_NBLOCKS
);
260 * Both ufs_iget_root and ufs_iget callback based on ufs type.
262 static struct inode
*
263 ufs_iget_root(struct fs_info
*fs
)
265 return UFS_SB(fs
)->ufs_iget_by_inr(fs
, UFS_ROOT_INODE
);
268 static struct inode
*
269 ufs_iget(const char *dname
, struct inode
*parent
)
271 const struct ufs_dir_entry
*dir
;
272 struct fs_info
*fs
= parent
->fs
;
274 dir
= ufs_find_entry(fs
, parent
, dname
);
278 return UFS_SB(fs
)->ufs_iget_by_inr(fs
, dir
->inode_value
);
281 static void ufs1_read_blkaddrs(struct inode
*inode
, char *buf
)
283 uint32_t dest
[UFS_NBLOCKS
];
284 const uint64_t *source
= (uint64_t *) (inode
->pvt
);
287 /* Convert ufs_inode_pvt uint64_t fields into uint32_t
288 * Upper-half part of ufs1 private blk addrs are always supposed to be
289 * zero (it's previosuly extended by us), thus data isn't being lost. */
290 for (i
= 0; i
< UFS_NBLOCKS
; i
++) {
291 if ((source
[i
] >> 32) != 0) {
292 /* This should never happen, but will not prevent anything
294 ufs_debug("ufs1: inode->pvt[%d]: warning!\n", i
);
297 dest
[i
] = (uint32_t)(source
[i
] & 0xFFFFFFFF);
299 memcpy(buf
, (const char *) dest
, inode
->size
);
302 static void ufs2_read_blkaddrs(struct inode
*inode
, char *buf
)
304 memcpy(buf
, (const char *) (inode
->pvt
), inode
->size
);
308 * Taken from ext2/ext2.c.
309 * Read the entire contents of an inode into a memory buffer
311 static int cache_get_file(struct inode
*inode
, void *buf
, size_t bytes
)
313 struct fs_info
*fs
= inode
->fs
;
314 size_t block_size
= BLOCK_SIZE(fs
);
315 uint32_t index
= 0; /* Logical block number */
320 if (inode
->size
> bytes
)
324 chunk
= min(bytes
, block_size
);
325 data
= ufs_get_cache(inode
, index
++);
326 memcpy(p
, data
, chunk
);
335 static int ufs_readlink(struct inode
*inode
, char *buf
)
337 struct fs_info
*fs
= inode
->fs
;
338 uint32_t i_symlink_limit
;
340 if (inode
->size
> BLOCK_SIZE(fs
))
341 return -1; /* Error! */
343 // TODO: use UFS_SB(fs)->maxlen_isymlink instead.
344 i_symlink_limit
= ((UFS_SB(fs
)->fs_type
== UFS1
) ?
345 sizeof(uint32_t) : sizeof(uint64_t)) * UFS_NBLOCKS
;
346 ufs_debug("UFS_SB(fs)->maxlen_isymlink=%d", UFS_SB(fs
)->maxlen_isymlink
);
348 if (inode
->size
<= i_symlink_limit
)
349 UFS_SB(fs
)->ufs_read_blkaddrs(inode
, buf
);
351 cache_get_file(inode
, buf
, inode
->size
);
356 static inline enum dir_type_flags
get_inode_mode(uint8_t type
)
359 case UFS_DTYPE_FIFO
: return DT_FIFO
;
360 case UFS_DTYPE_CHARDEV
: return DT_CHR
;
361 case UFS_DTYPE_DIR
: return DT_DIR
;
362 case UFS_DTYPE_BLOCK
: return DT_BLK
;
363 case UFS_DTYPE_RFILE
: return DT_REG
;
364 case UFS_DTYPE_SYMLINK
: return DT_LNK
;
365 case UFS_DTYPE_SOCKET
: return DT_SOCK
;
366 case UFS_DTYPE_WHITEOUT
: return DT_WHT
;
367 default: return DT_UNKNOWN
;
372 * Read one directory entry at a time
374 static int ufs_readdir(struct file
*file
, struct dirent
*dirent
)
376 struct fs_info
*fs
= file
->fs
;
377 struct inode
*inode
= file
->inode
;
378 const struct ufs_dir_entry
*dir
;
380 block_t index
= file
->offset
>> fs
->block_shift
;
382 if (file
->offset
>= inode
->size
)
383 return -1; /* End of file */
385 data
= ufs_get_cache(inode
, index
);
386 dir
= (const struct ufs_dir_entry
*)
387 (data
+ (file
->offset
& (BLOCK_SIZE(fs
) - 1)));
389 dirent
->d_ino
= dir
->inode_value
;
390 dirent
->d_off
= file
->offset
;
391 dirent
->d_reclen
= offsetof(struct dirent
, d_name
) + dir
->name_length
+ 1;
392 dirent
->d_type
= get_inode_mode(dir
->file_type
& 0x0F);
393 memcpy(dirent
->d_name
, dir
->name
, dir
->name_length
);
394 dirent
->d_name
[dir
->name_length
] = '\0';
396 file
->offset
+= dir
->dir_entry_len
; /* Update for next reading */
401 static inline struct ufs_sb_info
*
402 set_ufs_info(struct ufs_super_block
*sb
, int ufs_type
)
404 struct ufs_sb_info
*sbi
;
406 sbi
= malloc(sizeof *sbi
);
408 malloc_error("ufs_sb_info structure");
410 /* Setting up UFS-dependent info */
411 if (ufs_type
== UFS1
) {
412 sbi
->inode_size
= sizeof (struct ufs1_inode
);
413 sbi
->groups_count
= sb
->ufs1
.nr_frags
/ sb
->frags_per_cg
;
414 sbi
->ufs1
.delta_value
= sb
->ufs1
.delta_value
;
415 sbi
->ufs1
.cycle_mask
= sb
->ufs1
.cycle_mask
;
416 sbi
->ufs_iget_by_inr
= ufs1_iget_by_inr
;
417 sbi
->ufs_read_blkaddrs
= ufs1_read_blkaddrs
;
418 sbi
->addr_shift
= UFS1_ADDR_SHIFT
;
419 } else { // UFS2 or UFS2_PIGGY
420 sbi
->inode_size
= sizeof (struct ufs2_inode
);
421 sbi
->groups_count
= sb
->ufs2
.nr_frags
/ sb
->frags_per_cg
;
422 sbi
->ufs_iget_by_inr
= ufs2_iget_by_inr
;
423 sbi
->ufs_read_blkaddrs
= ufs2_read_blkaddrs
;
424 sbi
->addr_shift
= UFS2_ADDR_SHIFT
;
426 sbi
->inodes_per_block
= sb
->block_size
/ sbi
->inode_size
;
427 sbi
->inodes_per_cg
= sb
->inodes_per_cg
;
428 sbi
->blocks_per_cg
= sb
->frags_per_cg
>> sb
->c_blk_frag_shift
;
429 sbi
->off_inode_tbl
= sb
->off_inode_tbl
>> sb
->c_blk_frag_shift
;
430 sbi
->c_blk_frag_shift
= sb
->c_blk_frag_shift
;
431 sbi
->maxlen_isymlink
= sb
->maxlen_isymlink
;
432 sbi
->fs_type
= ufs_type
;
438 * Init the fs metadata and return block size
440 static int ufs_fs_init(struct fs_info
*fs
)
442 struct disk
*disk
= fs
->fs_dev
->disk
;
443 struct ufs_super_block sb
;
446 int ufs_type
= ufs_checksb(&sb
, disk
);
447 if (ufs_type
== NONE
)
450 ufs_debug("%s SB FOUND!\n", ufs_type
== UFS1
? "UFS1" : "UFS2");
451 ufs_debug("Block size: %u\n", sb
.block_size
);
453 fs
->fs_info
= (struct ufs_sb_info
*) set_ufs_info(&sb
, ufs_type
);
454 fs
->sector_shift
= disk
->sector_shift
;
455 fs
->sector_size
= disk
->sector_size
;
456 fs
->block_shift
= sb
.block_shift
;
457 fs
->block_size
= sb
.block_size
;
459 /* Initialize the cache, and force a clean on block zero */
460 cache_init(fs
->fs_dev
, sb
.block_shift
);
461 cs
= _get_cache_block(fs
->fs_dev
, 0);
462 memset(cs
->data
, 0, fs
->block_size
);
463 cache_lock_block(cs
);
465 /* For debug purposes */
469 return fs
->block_shift
;
472 const struct fs_ops ufs_fs_ops
= {
474 .fs_flags
= FS_USEMEM
| FS_THISIND
,
475 .fs_init
= ufs_fs_init
,
477 .getfssec
= generic_getfssec
,
478 .close_file
= generic_close_file
,
479 .mangle_name
= generic_mangle_name
,
480 .open_config
= generic_open_config
,
481 .readlink
= ufs_readlink
,
482 .readdir
= ufs_readdir
,
483 .iget_root
= ufs_iget_root
,
485 .next_extent
= ufs_next_extent
,