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
do_checksb(struct ufs_super_block * sb,struct disk * disk,const uint32_t sblock_off,const uint32_t ufs_smagic)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
ufs_checksb(struct ufs_super_block * sb,struct disk * disk)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 *
ufs_get_cache(struct inode * inode,block_t lblock)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 *
ufs_find_entry(struct fs_info * fs,struct inode * inode,const char * dname)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 *
ufs_get_inode(struct fs_info * fs,int inr)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 *
ufs1_iget_by_inr(struct fs_info * fs,uint32_t inr)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 *
ufs2_iget_by_inr(struct fs_info * fs,uint32_t inr)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 *
ufs_iget_root(struct fs_info * fs)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 *
ufs_iget(const char * dname,struct inode * parent)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
ufs1_read_blkaddrs(struct inode * inode,char * buf)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
ufs2_read_blkaddrs(struct inode * inode,char * buf)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 */
cache_get_file(struct inode * inode,void * buf,size_t bytes)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
ufs_readlink(struct inode * inode,char * buf)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
get_inode_mode(uint8_t type)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 */
ufs_readdir(struct file * file,struct dirent * dirent)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 *
set_ufs_info(struct ufs_super_block * sb,int ufs_type)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 */
ufs_fs_init(struct fs_info * fs)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 };
487