diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/btrfs/btrfs.c | 199 | ||||
| -rw-r--r-- | fs/btrfs/ctree.h | 7 | ||||
| -rw-r--r-- | fs/btrfs/dir-item.c | 74 | ||||
| -rw-r--r-- | fs/fat/Kconfig | 2 | ||||
| -rw-r--r-- | fs/fat/fat.c | 2 | ||||
| -rw-r--r-- | fs/fat/fat_write.c | 4 | ||||
| -rw-r--r-- | fs/fs.c | 6 | ||||
| -rw-r--r-- | fs/squashfs/sqfs.c | 2 | ||||
| -rw-r--r-- | fs/ubifs/super.c | 2 | ||||
| -rw-r--r-- | fs/ubifs/ubifs.c | 73 |
10 files changed, 189 insertions, 182 deletions
diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c index f3087f690fa..e663dda12e8 100644 --- a/fs/btrfs/btrfs.c +++ b/fs/btrfs/btrfs.c @@ -8,114 +8,15 @@ #include <config.h> #include <malloc.h> #include <u-boot/uuid.h> +#include <linux/kernel.h> #include <linux/time.h> +#include <fs.h> #include "btrfs.h" #include "crypto/hash.h" #include "disk-io.h" struct btrfs_fs_info *current_fs_info; -static int show_dir(struct btrfs_root *root, struct extent_buffer *eb, - struct btrfs_dir_item *di) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_inode_item ii; - struct btrfs_key key; - static const char* dir_item_str[] = { - [BTRFS_FT_REG_FILE] = " ", - [BTRFS_FT_DIR] = "DIR", - [BTRFS_FT_CHRDEV] = "CHR", - [BTRFS_FT_BLKDEV] = "BLK", - [BTRFS_FT_FIFO] = "FIF", - [BTRFS_FT_SOCK] = "SCK", - [BTRFS_FT_SYMLINK] = "SYM", - }; - u8 type = btrfs_dir_type(eb, di); - char namebuf[BTRFS_NAME_LEN]; - char *target = NULL; - char filetime[32]; - time_t mtime; - int ret = 0; - - /* skip XATTRs in directory listing */ - if (type == BTRFS_FT_XATTR) - return 0; - - btrfs_dir_item_key_to_cpu(eb, di, &key); - - if (key.type == BTRFS_ROOT_ITEM_KEY) { - struct btrfs_root *subvol; - - /* It's a subvolume, get its mtime from root item */ - subvol = btrfs_read_fs_root(fs_info, &key); - if (IS_ERR(subvol)) { - ret = PTR_ERR(subvol); - error("Can't find root %llu", key.objectid); - return ret; - } - mtime = btrfs_stack_timespec_sec(&subvol->root_item.otime); - } else { - struct btrfs_path path; - - /* It's regular inode, get its mtime from inode item */ - btrfs_init_path(&path); - ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - if (ret > 0) - ret = -ENOENT; - if (ret < 0) { - error("Can't find inode %llu", key.objectid); - btrfs_release_path(&path); - return ret; - } - read_extent_buffer(path.nodes[0], &ii, - btrfs_item_ptr_offset(path.nodes[0], path.slots[0]), - sizeof(ii)); - btrfs_release_path(&path); - mtime = btrfs_stack_timespec_sec(&ii.mtime); - } - ctime_r(&mtime, filetime); - - if (type == BTRFS_FT_SYMLINK) { - target = malloc(fs_info->sectorsize); - if (!target) { - error("Can't alloc memory for symlink %llu", - key.objectid); - return -ENOMEM; - } - ret = btrfs_readlink(root, key.objectid, target); - if (ret < 0) { - error("Failed to read symlink %llu", key.objectid); - goto out; - } - target[ret] = '\0'; - } - - if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type]) - printf("<%s> ", dir_item_str[type]); - else - printf("?%3u? ", type); - if (type == BTRFS_FT_CHRDEV || type == BTRFS_FT_BLKDEV) { - ASSERT(key.type == BTRFS_INODE_ITEM_KEY); - printf("%4llu,%5llu ", btrfs_stack_inode_rdev(&ii) >> 20, - btrfs_stack_inode_rdev(&ii) & 0xfffff); - } else { - if (key.type == BTRFS_INODE_ITEM_KEY) - printf("%10llu ", btrfs_stack_inode_size(&ii)); - else - printf("%10llu ", 0ULL); - } - - read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), - btrfs_dir_name_len(eb, di)); - printf("%24.24s %.*s", filetime, btrfs_dir_name_len(eb, di), namebuf); - if (type == BTRFS_FT_SYMLINK) - printf(" -> %s", target ? target : "?"); - printf("\n"); -out: - free(target); - return ret; -} - int btrfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition) { @@ -131,34 +32,102 @@ int btrfs_probe(struct blk_desc *fs_dev_desc, return ret; } -int btrfs_ls(const char *path) +/* + * The fs layer closes and re-probes btrfs between readdir() calls (see + * fs_readdir() in fs/fs.c), freeing and reallocating fs_info, so root cannot + * be stored directly. The subvolume id and inode number are stable though, so + * re-resolve the root from the current fs_info by subvolume id, which avoids + * a full path walk and is much faster. + */ +struct btrfs_dir_stream { + struct fs_dir_stream parent; + struct fs_dirent dirent; + u64 subvolid; + u64 ino; + u64 offset; +}; + +int btrfs_opendir(const char *dirname, struct fs_dir_stream **dirsp) { struct btrfs_fs_info *fs_info = current_fs_info; - struct btrfs_root *root = fs_info->fs_root; - u64 ino = BTRFS_FIRST_FREE_OBJECTID; + struct btrfs_dir_stream *dirs; + struct btrfs_root *root; + u64 ino; u8 type; int ret; + *dirsp = NULL; ASSERT(fs_info); + ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, - path, &root, &ino, &type, 40); - if (ret < 0) { - printf("Cannot lookup path %s\n", path); + dirname, &root, &ino, &type, 40); + if (ret < 0) return ret; - } + if (type != BTRFS_FT_DIR) + return -ENOTDIR; - if (type != BTRFS_FT_DIR) { - error("Not a directory: %s", path); - return -ENOENT; + dirs = calloc(1, sizeof(*dirs)); + if (!dirs) + return -ENOMEM; + dirs->subvolid = root->root_key.objectid; + dirs->ino = ino; + + *dirsp = &dirs->parent; + return 0; +} + +static unsigned int btrfs_dirent_type_to_fs_type(u8 dirent_type) +{ + switch (dirent_type) { + case BTRFS_FT_DIR: + return FS_DT_DIR; + case BTRFS_FT_SYMLINK: + return FS_DT_LNK; + default: + return FS_DT_REG; } - ret = btrfs_iter_dir(root, ino, show_dir); - if (ret < 0) { - error("An error occurred while listing directory %s", path); +} + +int btrfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) +{ + struct btrfs_dir_stream *dirs = container_of(fs_dirs, struct btrfs_dir_stream, parent); + struct btrfs_fs_info *fs_info = current_fs_info; + struct fs_dirent *dent = &dirs->dirent; + struct btrfs_root *root; + struct btrfs_key key; + u8 type; + int ret; + + *dentp = NULL; + ASSERT(fs_info); + + key.objectid = dirs->subvolid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + root = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(root)) + return PTR_ERR(root); + + memset(dent, 0, sizeof(*dent)); + ret = btrfs_next_dir_entry(root, dirs->ino, &dirs->offset, dent->name, + sizeof(dent->name), &type); + if (ret < 0) return ret; - } + if (ret > 0) + return -ENOENT; + + dent->type = btrfs_dirent_type_to_fs_type(type); + *dentp = dent; return 0; } +void btrfs_closedir(struct fs_dir_stream *fs_dirs) +{ + struct btrfs_dir_stream *dirs = container_of(fs_dirs, struct btrfs_dir_stream, parent); + + free(dirs); +} + int btrfs_exists(const char *file) { struct btrfs_fs_info *fs_info = current_fs_info; diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index b7be09d5df8..3fa9a8c9c02 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1220,11 +1220,8 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, struct btrfs_path *path, u64 dir, const char *name, int name_len, int mod); -typedef int (*btrfs_iter_dir_callback_t)(struct btrfs_root *root, - struct extent_buffer *eb, - struct btrfs_dir_item *di); -int btrfs_iter_dir(struct btrfs_root *root, u64 ino, - btrfs_iter_dir_callback_t callback); +int btrfs_next_dir_entry(struct btrfs_root *root, u64 ino, u64 *offset, + char *namebuf, int namebuf_len, u8 *ftype); /* inode.c */ int btrfs_lookup_path(struct btrfs_root *root, u64 ino, const char *filename, struct btrfs_root **root_ret, u64 *ino_ret, diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 5f81d6414f0..c7b87d60d98 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -114,54 +114,74 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, return btrfs_match_dir_item_name(root, path, name, name_len); } -int btrfs_iter_dir(struct btrfs_root *root, u64 ino, - btrfs_iter_dir_callback_t callback) +/* + * btrfs_next_dir_entry() - read one directory entry at or after a cursor + * + * Iterator for the fs-layer readdir, which is re-entered once per entry: + * returns the first real entry whose BTRFS_DIR_INDEX_KEY offset is + * >= *offset and advances *offset past it. + * + * @root, @ino: directory to read + * @offset: in/out cursor; DIR_INDEX offset to resume from + * @namebuf: caller buffer that receives the NUL-terminated name + * @namebuf_len: size of @namebuf in bytes + * @ftype: receives the BTRFS_FT_* type of the entry + * + * Return: 0 if an entry was returned, 1 when the directory is exhausted, + * -ve on error. + */ +int btrfs_next_dir_entry(struct btrfs_root *root, u64 ino, u64 *offset, + char *namebuf, int namebuf_len, u8 *ftype) { struct btrfs_path path; struct btrfs_key key; + struct btrfs_dir_item *di; + int name_len; int ret; btrfs_init_path(&path); key.objectid = ino; key.type = BTRFS_DIR_INDEX_KEY; - key.offset = 0; + key.offset = *offset; ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) - return ret; - /* Should not happen */ - if (ret == 0) { - ret = -EUCLEAN; goto out; - } + if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) { ret = btrfs_next_leaf(root, &path); if (ret < 0) goto out; - if (ret > 0) { - ret = 0; + if (ret > 0) { /* end of tree */ + ret = 1; goto out; } } - do { - struct btrfs_dir_item *di; - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - if (key.objectid != ino || key.type != BTRFS_DIR_INDEX_KEY) - break; - di = btrfs_item_ptr(path.nodes[0], path.slots[0], - struct btrfs_dir_item); - if (verify_dir_item(root, path.nodes[0], di)) { - ret = -EUCLEAN; - goto out; - } - ret = callback(root, path.nodes[0], di); - if (ret < 0) - goto out; - } while (!(ret = btrfs_next_item(root, &path))); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.objectid != ino || key.type != BTRFS_DIR_INDEX_KEY) { + ret = 1; /* no more entries for this dir */ + goto out; + } + + di = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_dir_item); + if (verify_dir_item(root, path.nodes[0], di)) { + ret = -EUCLEAN; + goto out; + } + + *offset = key.offset + 1; + + name_len = btrfs_dir_name_len(path.nodes[0], di); + if (name_len > namebuf_len - 1) + name_len = namebuf_len - 1; + read_extent_buffer(path.nodes[0], namebuf, + (unsigned long)(di + 1), name_len); + namebuf[name_len] = '\0'; + *ftype = btrfs_dir_type(path.nodes[0], di); + ret = 0; - if (ret > 0) - ret = 0; out: btrfs_release_path(&path); return ret; diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 9606fa48bbe..aa121844844 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -36,4 +36,4 @@ config FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH depends on FS_FAT help Handle filesystems on media where the hardware block size and - the sector size in the FAT metadata do not match. + the sector size in the FAT metadata do not match. diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 31c136e3b9e..c1ccf30771a 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -71,7 +71,7 @@ static inline __u32 sect_to_block(__u32 sect, __u32 *off) static int disk_rw(__u32 sect, __u32 nr_sect, void *buf, bool read) { - int ret; + int ret = 0; __u8 *block = NULL; __u32 rem, size, s, n; const ulong blksz = cur_part_info.blksz; diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index c98b530f747..a1d28b4e305 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -1175,9 +1175,9 @@ static void dentry_set_time(dir_entry *dentptr) date = (tm.tm_mday & 0x1f) | ((tm.tm_mon & 0xf) << 5) | ((tm.tm_year - 1980) << 9); - time = (tm.tm_sec > 1) | + time = (tm.tm_sec >> 1) | ((tm.tm_min & 0x3f) << 5) | - (tm.tm_hour << 11); + (tm.tm_hour << 11); dentptr->date = date; dentptr->time = time; return; @@ -320,13 +320,15 @@ static struct fstype_info fstypes[] = { .null_dev_desc_ok = false, .probe = btrfs_probe, .close = btrfs_close, - .ls = btrfs_ls, + .ls = fs_ls_generic, .exists = btrfs_exists, .size = btrfs_size, .read = btrfs_read, .write = fs_write_unsupported, .uuid = btrfs_uuid, - .opendir = fs_opendir_unsupported, + .opendir = btrfs_opendir, + .readdir = btrfs_readdir, + .closedir = btrfs_closedir, .unlink = fs_unlink_unsupported, .mkdir = fs_mkdir_unsupported, .ln = fs_ln_unsupported, diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c index 543db8c7e9e..0768fc4a7b2 100644 --- a/fs/squashfs/sqfs.c +++ b/fs/squashfs/sqfs.c @@ -1636,6 +1636,8 @@ static int sqfs_read_nest(const char *filename, void *buf, loff_t offset, *actread = finfo.size; } + ret = 0; + out: free(fragment); free(datablock); diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index b6004b88f4e..9c8974a97a5 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -2694,7 +2694,7 @@ MODULE_VERSION(__stringify(UBIFS_VERSION)); MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter"); MODULE_DESCRIPTION("UBIFS - UBI File System"); #else -int uboot_ubifs_mount(char *vol_name) +int uboot_ubifs_mount(const char *vol_name) { struct dentry *ret; int flags; diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c index b0cc0d2e1b2..6121fb0b135 100644 --- a/fs/ubifs/ubifs.c +++ b/fs/ubifs/ubifs.c @@ -505,26 +505,32 @@ static unsigned long ubifs_findfile(struct super_block *sb, char *filename) { int ret; char *next; - char fpath[128]; - char symlinkpath[128]; - char *name = fpath; + char *buf; + char *name; unsigned long root_inum = 1; unsigned long inum; int symlink_count = 0; /* Don't allow symlink recursion */ - char link_name[64]; - - strcpy(fpath, filename); + size_t filenamelen; /* Remove all leading slashes */ - while (*name == '/') - name++; + while (*filename == '/') + filename++; + + filenamelen = strlen(filename); + buf = kmalloc(filenamelen + 1, GFP_NOFS); + if (!buf) + return -ENOMEM; + memcpy(buf, filename, filenamelen + 1); + name = buf; /* * Handle root-direcoty ('/') */ inum = root_inum; - if (!name || *name == '\0') + if (!name || *name == '\0') { + kfree(buf); return inum; + } for (;;) { struct inode *inode; @@ -537,41 +543,53 @@ static unsigned long ubifs_findfile(struct super_block *sb, char *filename) while (*next == '/') *(next++) = '\0'; } - ret = ubifs_finddir(sb, name, root_inum, &inum); - if (!ret) + if (ret <= 0) { + kfree(buf); return 0; + } inode = ubifs_iget(sb, inum); - if (!inode) + if (!inode) { + kfree(buf); return 0; + } ui = ubifs_inode(inode); if ((inode->i_mode & S_IFMT) == S_IFLNK) { - char buf[128]; + size_t newbufsize; + char *newbuf; + char *linkdata = ui->data; + size_t linklen = ui->data_len; /* We have some sort of symlink recursion, bail out */ if (symlink_count++ > 8) { ubifs_iput(inode); printf("Symlink recursion, aborting\n"); + kfree(buf); return 0; } - memcpy(link_name, ui->data, ui->data_len); - link_name[ui->data_len] = '\0'; - if (link_name[0] == '/') { - /* Absolute path, redo everything without - * the leading slash */ - next = name = link_name + 1; + while (linklen && *linkdata == '/') { + /* Absolute path, i.e. relative to root. */ root_inum = 1; + linkdata++; + linklen--; + } + newbufsize = + linklen + 1 + (next ? strlen(next) : 0) + 1; + newbuf = kmalloc(newbufsize, GFP_NOFS); + if (!newbuf) { + kfree(buf); ubifs_iput(inode); - continue; + return -ENOMEM; } - /* Relative to cur dir */ - sprintf(buf, "%s/%s", - link_name, next == NULL ? "" : next); - memcpy(symlinkpath, buf, sizeof(buf)); - next = name = symlinkpath; + + memcpy(newbuf, linkdata, linklen); + sprintf(newbuf + linklen, "/%s", next ?: ""); + kfree(buf); + buf = newbuf; + name = newbuf; ubifs_iput(inode); continue; } @@ -583,14 +601,13 @@ static unsigned long ubifs_findfile(struct super_block *sb, char *filename) /* Found the node! */ if (!next || *next == '\0') { ubifs_iput(inode); + kfree(buf); return inum; } root_inum = inum; name = next; } - - return 0; } int ubifs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info) @@ -974,7 +991,7 @@ void ubifs_close(void) } /* Compat wrappers for common/cmd_ubifs.c */ -int ubifs_load(char *filename, unsigned long addr, u32 size) +int ubifs_load(const char *filename, unsigned long addr, u32 size) { loff_t actread; int err; |
