1 | /* |
---|
2 | * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv@gmail.com> |
---|
3 | * |
---|
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. |
---|
8 | * |
---|
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. |
---|
13 | * |
---|
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. |
---|
18 | */ |
---|
19 | |
---|
20 | #include <dprintf.h> |
---|
21 | #include <stdio.h> |
---|
22 | #include <string.h> |
---|
23 | #include <sys/dirent.h> |
---|
24 | #include <cache.h> |
---|
25 | #include <disk.h> |
---|
26 | #include <fs.h> |
---|
27 | #include <minmax.h> |
---|
28 | #include "core.h" |
---|
29 | #include "ufs.h" |
---|
30 | |
---|
31 | /* |
---|
32 | * Read the super block and check magic fields based on |
---|
33 | * passed paramaters. |
---|
34 | */ |
---|
35 | static bool |
---|
36 | do_checksb(struct ufs_super_block *sb, struct disk *disk, |
---|
37 | const uint32_t sblock_off, const uint32_t ufs_smagic) |
---|
38 | { |
---|
39 | uint32_t lba; |
---|
40 | static uint32_t count; |
---|
41 | |
---|
42 | /* How many sectors are needed to fill sb struct */ |
---|
43 | if (!count) |
---|
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); |
---|
49 | |
---|
50 | if (sb->magic == ufs_smagic) |
---|
51 | return true; |
---|
52 | |
---|
53 | return false; |
---|
54 | } |
---|
55 | |
---|
56 | /* |
---|
57 | * Go through all possible ufs superblock offsets. |
---|
58 | * TODO: Add UFS support to removable media (sb offset: 0). |
---|
59 | */ |
---|
60 | static int |
---|
61 | ufs_checksb(struct ufs_super_block *sb, struct disk *disk) |
---|
62 | { |
---|
63 | /* Check for UFS1 sb */ |
---|
64 | if (do_checksb(sb, disk, UFS1_SBLOCK_OFFSET, UFS1_SUPER_MAGIC)) |
---|
65 | return UFS1; |
---|
66 | /* Check for UFS2 sb */ |
---|
67 | if (do_checksb(sb, disk, UFS2_SBLOCK_OFFSET, UFS2_SUPER_MAGIC)) |
---|
68 | return UFS2; |
---|
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)) |
---|
71 | return UFS2_PIGGY; |
---|
72 | |
---|
73 | return NONE; |
---|
74 | } |
---|
75 | |
---|
76 | /* |
---|
77 | * lblock stands for linear block address, |
---|
78 | * whereas pblock is the actual blk ptr to get data from. |
---|
79 | * |
---|
80 | * UFS1/2 use frag addrs rather than blk ones, then |
---|
81 | * the offset into the block must be calculated. |
---|
82 | */ |
---|
83 | static const void * |
---|
84 | ufs_get_cache(struct inode *inode, block_t lblock) |
---|
85 | { |
---|
86 | const void *data; |
---|
87 | struct fs_info *fs = inode->fs; |
---|
88 | struct ufs_sb_info *sb = UFS_SB(inode->fs); |
---|
89 | uint64_t frag_addr, frag_offset; |
---|
90 | uint32_t frag_shift; |
---|
91 | block_t pblock; |
---|
92 | |
---|
93 | frag_addr = ufs_bmap(inode, lblock, NULL); |
---|
94 | if (!frag_addr) |
---|
95 | return NULL; |
---|
96 | |
---|
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); |
---|
102 | /* Read the blk */ |
---|
103 | data = get_cache(fs->fs_dev, pblock); |
---|
104 | |
---|
105 | /* Return offset into block */ |
---|
106 | return data + (frag_offset & (fs->block_size - 1)); |
---|
107 | } |
---|
108 | |
---|
109 | /* |
---|
110 | * Based on fs/ext2/ext2.c |
---|
111 | * find a dir entry, return it if found, or return NULL. |
---|
112 | */ |
---|
113 | static const struct ufs_dir_entry * |
---|
114 | ufs_find_entry(struct fs_info *fs, struct inode *inode, const char *dname) |
---|
115 | { |
---|
116 | const struct ufs_dir_entry *dir; |
---|
117 | const char *data; |
---|
118 | int32_t i, offset, maxoffset; |
---|
119 | block_t index = 0; |
---|
120 | |
---|
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++); |
---|
124 | offset = 0; |
---|
125 | maxoffset = min(inode->size-i, fs->block_size); |
---|
126 | |
---|
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) |
---|
131 | break; |
---|
132 | |
---|
133 | /* |
---|
134 | * Name fields are variable-length and null terminated, |
---|
135 | * then it's possible to use strcmp directly. |
---|
136 | */ |
---|
137 | if (dir->inode_value && !strcmp(dname, (const char *)dir->name)) { |
---|
138 | ufs_debug("(found)\n"); |
---|
139 | return dir; |
---|
140 | } |
---|
141 | offset += dir->dir_entry_len; |
---|
142 | } |
---|
143 | } |
---|
144 | ufs_debug("(not found)\n"); |
---|
145 | return NULL; |
---|
146 | } |
---|
147 | |
---|
148 | /* |
---|
149 | * Get either UFS1/2 inode structures. |
---|
150 | */ |
---|
151 | static const void * |
---|
152 | ufs_get_inode(struct fs_info *fs, int inr) |
---|
153 | { |
---|
154 | const char *data; |
---|
155 | uint32_t group, inode_offset, inode_table; |
---|
156 | uint32_t block_num, block_off; |
---|
157 | |
---|
158 | /* Get cylinder group nr. */ |
---|
159 | group = inr / UFS_SB(fs)->inodes_per_cg; |
---|
160 | /* |
---|
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! |
---|
164 | */ |
---|
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); |
---|
169 | return NULL; |
---|
170 | } |
---|
171 | |
---|
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); |
---|
181 | |
---|
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; |
---|
185 | |
---|
186 | /* |
---|
187 | * Read the blk from the blk addr previously computed; |
---|
188 | * Calc the inode struct offset into the read block. |
---|
189 | */ |
---|
190 | data = get_cache(fs->fs_dev, block_num); |
---|
191 | return data + block_off * UFS_SB(fs)->inode_size; |
---|
192 | } |
---|
193 | |
---|
194 | static struct inode * |
---|
195 | ufs1_iget_by_inr(struct fs_info *fs, uint32_t inr) |
---|
196 | { |
---|
197 | const struct ufs1_inode *ufs_inode; |
---|
198 | struct inode *inode; |
---|
199 | uint64_t *dest; |
---|
200 | uint32_t *source; |
---|
201 | int i; |
---|
202 | |
---|
203 | ufs_inode = (struct ufs1_inode *) ufs_get_inode(fs, inr); |
---|
204 | if (!ufs_inode) |
---|
205 | return NULL; |
---|
206 | |
---|
207 | if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt)))) |
---|
208 | return NULL; |
---|
209 | |
---|
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; |
---|
218 | |
---|
219 | /* |
---|
220 | * Copy and extend blk pointers to 64 bits, so avoid |
---|
221 | * having two structures for inode private. |
---|
222 | */ |
---|
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; |
---|
227 | |
---|
228 | return inode; |
---|
229 | } |
---|
230 | |
---|
231 | static struct inode * |
---|
232 | ufs2_iget_by_inr(struct fs_info *fs, uint32_t inr) |
---|
233 | { |
---|
234 | const struct ufs2_inode *ufs_inode; |
---|
235 | struct inode *inode; |
---|
236 | |
---|
237 | ufs_inode = (struct ufs2_inode *) ufs_get_inode(fs, inr); |
---|
238 | if (!ufs_inode) |
---|
239 | return NULL; |
---|
240 | |
---|
241 | if (!(inode = alloc_inode(fs, inr, sizeof(struct ufs_inode_pvt)))) |
---|
242 | return NULL; |
---|
243 | |
---|
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); |
---|
255 | |
---|
256 | return inode; |
---|
257 | } |
---|
258 | |
---|
259 | /* |
---|
260 | * Both ufs_iget_root and ufs_iget callback based on ufs type. |
---|
261 | */ |
---|
262 | static struct inode * |
---|
263 | ufs_iget_root(struct fs_info *fs) |
---|
264 | { |
---|
265 | return UFS_SB(fs)->ufs_iget_by_inr(fs, UFS_ROOT_INODE); |
---|
266 | } |
---|
267 | |
---|
268 | static struct inode * |
---|
269 | ufs_iget(const char *dname, struct inode *parent) |
---|
270 | { |
---|
271 | const struct ufs_dir_entry *dir; |
---|
272 | struct fs_info *fs = parent->fs; |
---|
273 | |
---|
274 | dir = ufs_find_entry(fs, parent, dname); |
---|
275 | if (!dir) |
---|
276 | return NULL; |
---|
277 | |
---|
278 | return UFS_SB(fs)->ufs_iget_by_inr(fs, dir->inode_value); |
---|
279 | } |
---|
280 | |
---|
281 | static void ufs1_read_blkaddrs(struct inode *inode, char *buf) |
---|
282 | { |
---|
283 | uint32_t dest[UFS_NBLOCKS]; |
---|
284 | const uint64_t *source = (uint64_t *) (inode->pvt); |
---|
285 | int i; |
---|
286 | |
---|
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 |
---|
293 | * from working. */ |
---|
294 | ufs_debug("ufs1: inode->pvt[%d]: warning!\n", i); |
---|
295 | } |
---|
296 | |
---|
297 | dest[i] = (uint32_t)(source[i] & 0xFFFFFFFF); |
---|
298 | } |
---|
299 | memcpy(buf, (const char *) dest, inode->size); |
---|
300 | } |
---|
301 | |
---|
302 | static void ufs2_read_blkaddrs(struct inode *inode, char *buf) |
---|
303 | { |
---|
304 | memcpy(buf, (const char *) (inode->pvt), inode->size); |
---|
305 | } |
---|
306 | |
---|
307 | /* |
---|
308 | * Taken from ext2/ext2.c. |
---|
309 | * Read the entire contents of an inode into a memory buffer |
---|
310 | */ |
---|
311 | static int cache_get_file(struct inode *inode, void *buf, size_t bytes) |
---|
312 | { |
---|
313 | struct fs_info *fs = inode->fs; |
---|
314 | size_t block_size = BLOCK_SIZE(fs); |
---|
315 | uint32_t index = 0; /* Logical block number */ |
---|
316 | size_t chunk; |
---|
317 | const char *data; |
---|
318 | char *p = buf; |
---|
319 | |
---|
320 | if (inode->size > bytes) |
---|
321 | bytes = inode->size; |
---|
322 | |
---|
323 | while (bytes) { |
---|
324 | chunk = min(bytes, block_size); |
---|
325 | data = ufs_get_cache(inode, index++); |
---|
326 | memcpy(p, data, chunk); |
---|
327 | |
---|
328 | bytes -= chunk; |
---|
329 | p += chunk; |
---|
330 | } |
---|
331 | |
---|
332 | return 0; |
---|
333 | } |
---|
334 | |
---|
335 | static int ufs_readlink(struct inode *inode, char *buf) |
---|
336 | { |
---|
337 | struct fs_info *fs = inode->fs; |
---|
338 | uint32_t i_symlink_limit; |
---|
339 | |
---|
340 | if (inode->size > BLOCK_SIZE(fs)) |
---|
341 | return -1; /* Error! */ |
---|
342 | |
---|
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); |
---|
347 | |
---|
348 | if (inode->size <= i_symlink_limit) |
---|
349 | UFS_SB(fs)->ufs_read_blkaddrs(inode, buf); |
---|
350 | else |
---|
351 | cache_get_file(inode, buf, inode->size); |
---|
352 | |
---|
353 | return inode->size; |
---|
354 | } |
---|
355 | |
---|
356 | static inline enum dir_type_flags get_inode_mode(uint8_t type) |
---|
357 | { |
---|
358 | switch(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; |
---|
368 | } |
---|
369 | } |
---|
370 | |
---|
371 | /* |
---|
372 | * Read one directory entry at a time |
---|
373 | */ |
---|
374 | static int ufs_readdir(struct file *file, struct dirent *dirent) |
---|
375 | { |
---|
376 | struct fs_info *fs = file->fs; |
---|
377 | struct inode *inode = file->inode; |
---|
378 | const struct ufs_dir_entry *dir; |
---|
379 | const char *data; |
---|
380 | block_t index = file->offset >> fs->block_shift; |
---|
381 | |
---|
382 | if (file->offset >= inode->size) |
---|
383 | return -1; /* End of file */ |
---|
384 | |
---|
385 | data = ufs_get_cache(inode, index); |
---|
386 | dir = (const struct ufs_dir_entry *) |
---|
387 | (data + (file->offset & (BLOCK_SIZE(fs) - 1))); |
---|
388 | |
---|
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'; |
---|
395 | |
---|
396 | file->offset += dir->dir_entry_len; /* Update for next reading */ |
---|
397 | |
---|
398 | return 0; |
---|
399 | } |
---|
400 | |
---|
401 | static inline struct ufs_sb_info * |
---|
402 | set_ufs_info(struct ufs_super_block *sb, int ufs_type) |
---|
403 | { |
---|
404 | struct ufs_sb_info *sbi; |
---|
405 | |
---|
406 | sbi = malloc(sizeof *sbi); |
---|
407 | if (!sbi) |
---|
408 | malloc_error("ufs_sb_info structure"); |
---|
409 | |
---|
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; |
---|
425 | } |
---|
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; |
---|
433 | |
---|
434 | return sbi; |
---|
435 | } |
---|
436 | |
---|
437 | /* |
---|
438 | * Init the fs metadata and return block size |
---|
439 | */ |
---|
440 | static int ufs_fs_init(struct fs_info *fs) |
---|
441 | { |
---|
442 | struct disk *disk = fs->fs_dev->disk; |
---|
443 | struct ufs_super_block sb; |
---|
444 | struct cache *cs; |
---|
445 | |
---|
446 | int ufs_type = ufs_checksb(&sb, disk); |
---|
447 | if (ufs_type == NONE) |
---|
448 | return -1; |
---|
449 | |
---|
450 | ufs_debug("%s SB FOUND!\n", ufs_type == UFS1 ? "UFS1" : "UFS2"); |
---|
451 | ufs_debug("Block size: %u\n", sb.block_size); |
---|
452 | |
---|
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; |
---|
458 | |
---|
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); |
---|
464 | |
---|
465 | /* For debug purposes */ |
---|
466 | //ufs_checking(fs); |
---|
467 | |
---|
468 | //return -1; |
---|
469 | return fs->block_shift; |
---|
470 | } |
---|
471 | |
---|
472 | const struct fs_ops ufs_fs_ops = { |
---|
473 | .fs_name = "ufs", |
---|
474 | .fs_flags = FS_USEMEM | FS_THISIND, |
---|
475 | .fs_init = ufs_fs_init, |
---|
476 | .searchdir = NULL, |
---|
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, |
---|
484 | .iget = ufs_iget, |
---|
485 | .next_extent = ufs_next_extent, |
---|
486 | }; |
---|