1030 lines · 25.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * linux/fs/befs/linuxvfs.c * * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/fs_context.h>#include <linux/fs_parser.h>#include <linux/errno.h>#include <linux/filelock.h>#include <linux/stat.h>#include <linux/nls.h>#include <linux/buffer_head.h>#include <linux/vfs.h>#include <linux/namei.h>#include <linux/sched.h>#include <linux/cred.h>#include <linux/exportfs.h>#include <linux/seq_file.h>#include <linux/blkdev.h> #include "befs.h"#include "btree.h"#include "inode.h"#include "datastream.h"#include "super.h"#include "io.h" MODULE_DESCRIPTION("BeOS File System (BeFS) driver");MODULE_AUTHOR("Will Dyson");MODULE_LICENSE("GPL"); /* The units the vfs expects inode->i_blocks to be in */#define VFS_BLOCK_SIZE 512 static int befs_readdir(struct file *, struct dir_context *);static int befs_get_block(struct inode *, sector_t, struct buffer_head *, int);static int befs_read_folio(struct file *file, struct folio *folio);static sector_t befs_bmap(struct address_space *mapping, sector_t block);static struct dentry *befs_lookup(struct inode *, struct dentry *,				  unsigned int);static struct inode *befs_iget(struct super_block *, unsigned long);static struct inode *befs_alloc_inode(struct super_block *sb);static void befs_free_inode(struct inode *inode);static void befs_destroy_inodecache(void);static int befs_symlink_read_folio(struct file *, struct folio *);static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,			char **out, int *out_len);static int befs_nls2utf(struct super_block *sb, const char *in, int in_len,			char **out, int *out_len);static void befs_put_super(struct super_block *);static int befs_statfs(struct dentry *, struct kstatfs *);static int befs_show_options(struct seq_file *, struct dentry *);static struct dentry *befs_fh_to_dentry(struct super_block *sb,				struct fid *fid, int fh_len, int fh_type);static struct dentry *befs_fh_to_parent(struct super_block *sb,				struct fid *fid, int fh_len, int fh_type);static struct dentry *befs_get_parent(struct dentry *child);static void befs_free_fc(struct fs_context *fc); static const struct super_operations befs_sops = {	.alloc_inode	= befs_alloc_inode,	/* allocate a new inode */	.free_inode	= befs_free_inode, /* deallocate an inode */	.put_super	= befs_put_super,	/* uninit super */	.statfs		= befs_statfs,	/* statfs */	.show_options	= befs_show_options,}; /* slab cache for befs_inode_info objects */static struct kmem_cache *befs_inode_cachep; static const struct file_operations befs_dir_operations = {	.read		= generic_read_dir,	.iterate_shared	= befs_readdir,	.llseek		= generic_file_llseek,	.setlease	= generic_setlease,}; static const struct inode_operations befs_dir_inode_operations = {	.lookup		= befs_lookup,}; static const struct address_space_operations befs_aops = {	.read_folio	= befs_read_folio,	.bmap		= befs_bmap,}; static const struct address_space_operations befs_symlink_aops = {	.read_folio	= befs_symlink_read_folio,}; static const struct export_operations befs_export_operations = {	.encode_fh	= generic_encode_ino32_fh,	.fh_to_dentry	= befs_fh_to_dentry,	.fh_to_parent	= befs_fh_to_parent,	.get_parent	= befs_get_parent,}; /* * Called by generic_file_read() to read a folio of data * * In turn, simply calls a generic block read function and * passes it the address of befs_get_block, for mapping file * positions to disk blocks. */static int befs_read_folio(struct file *file, struct folio *folio){	return block_read_full_folio(folio, befs_get_block);} static sector_tbefs_bmap(struct address_space *mapping, sector_t block){	return generic_block_bmap(mapping, block, befs_get_block);} /* * Generic function to map a file position (block) to a * disk offset (passed back in bh_result). * * Used by many higher level functions. * * Calls befs_fblock2brun() in datastream.c to do the real work. */ static intbefs_get_block(struct inode *inode, sector_t block,	       struct buffer_head *bh_result, int create){	struct super_block *sb = inode->i_sb;	befs_data_stream *ds = &BEFS_I(inode)->i_data.ds;	befs_block_run run = BAD_IADDR;	int res;	ulong disk_off; 	befs_debug(sb, "---> befs_get_block() for inode %llu, block %ld",		   inode->i_ino, (long)block);	if (create) {		befs_error(sb, "befs_get_block() was asked to write to "			   "block %ld in inode %llu", (long)block,			   inode->i_ino);		return -EPERM;	} 	res = befs_fblock2brun(sb, ds, block, &run);	if (res != BEFS_OK) {		befs_error(sb,			   "<--- %s for inode %llu, block %ld ERROR",			   __func__, inode->i_ino,			   (long)block);		return -EFBIG;	} 	disk_off = (ulong) iaddr2blockno(sb, &run); 	map_bh(bh_result, inode->i_sb, disk_off); 	befs_debug(sb, "<--- %s for inode %llu, block %ld, disk address %lu",		  __func__, inode->i_ino, (long)block,		  (unsigned long)disk_off); 	return 0;} static struct dentry *befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags){	struct inode *inode;	struct super_block *sb = dir->i_sb;	const befs_data_stream *ds = &BEFS_I(dir)->i_data.ds;	befs_off_t offset;	int ret;	int utfnamelen;	char *utfname;	const char *name = dentry->d_name.name; 	befs_debug(sb, "---> %s name %pd inode %llu", __func__,		   dentry, dir->i_ino); 	/* Convert to UTF-8 */	if (BEFS_SB(sb)->nls) {		ret =		    befs_nls2utf(sb, name, strlen(name), &utfname, &utfnamelen);		if (ret < 0) {			befs_debug(sb, "<--- %s ERROR", __func__);			return ERR_PTR(ret);		}		ret = befs_btree_find(sb, ds, utfname, &offset);		kfree(utfname); 	} else {		ret = befs_btree_find(sb, ds, name, &offset);	} 	if (ret == BEFS_BT_NOT_FOUND) {		befs_debug(sb, "<--- %s %pd not found", __func__, dentry);		inode = NULL;	} else if (ret != BEFS_OK || offset == 0) {		befs_error(sb, "<--- %s Error", __func__);		inode = ERR_PTR(-ENODATA);	} else {		inode = befs_iget(dir->i_sb, (ino_t) offset);	}	befs_debug(sb, "<--- %s", __func__); 	return d_splice_alias(inode, dentry);} static intbefs_readdir(struct file *file, struct dir_context *ctx){	struct inode *inode = file_inode(file);	struct super_block *sb = inode->i_sb;	const befs_data_stream *ds = &BEFS_I(inode)->i_data.ds;	befs_off_t value;	int result;	size_t keysize;	char keybuf[BEFS_NAME_LEN + 1]; 	befs_debug(sb, "---> %s name %pD, inode %llu, ctx->pos %lld",		  __func__, file, inode->i_ino, ctx->pos); 	while (1) {		result = befs_btree_read(sb, ds, ctx->pos, BEFS_NAME_LEN + 1,					 keybuf, &keysize, &value); 		if (result == BEFS_ERR) {			befs_debug(sb, "<--- %s ERROR", __func__);			befs_error(sb, "IO error reading %pD (inode %llu)",				   file, inode->i_ino);			return -EIO; 		} else if (result == BEFS_BT_END) {			befs_debug(sb, "<--- %s END", __func__);			return 0; 		} else if (result == BEFS_BT_EMPTY) {			befs_debug(sb, "<--- %s Empty directory", __func__);			return 0;		} 		/* Convert to NLS */		if (BEFS_SB(sb)->nls) {			char *nlsname;			int nlsnamelen; 			result =			    befs_utf2nls(sb, keybuf, keysize, &nlsname,					 &nlsnamelen);			if (result < 0) {				befs_debug(sb, "<--- %s ERROR", __func__);				return result;			}			if (!dir_emit(ctx, nlsname, nlsnamelen,				      (ino_t) value, DT_UNKNOWN)) {				kfree(nlsname);				return 0;			}			kfree(nlsname);		} else {			if (!dir_emit(ctx, keybuf, keysize,				      (ino_t) value, DT_UNKNOWN))				return 0;		}		ctx->pos++;	}} static struct inode *befs_alloc_inode(struct super_block *sb){	struct befs_inode_info *bi; 	bi = alloc_inode_sb(sb, befs_inode_cachep, GFP_KERNEL);	if (!bi)		return NULL;	return &bi->vfs_inode;} static void befs_free_inode(struct inode *inode){	kmem_cache_free(befs_inode_cachep, BEFS_I(inode));} static void init_once(void *foo){	struct befs_inode_info *bi = (struct befs_inode_info *) foo; 	inode_init_once(&bi->vfs_inode);} static struct inode *befs_iget(struct super_block *sb, unsigned long ino){	struct buffer_head *bh;	befs_inode *raw_inode;	struct befs_sb_info *befs_sb = BEFS_SB(sb);	struct befs_inode_info *befs_ino;	struct inode *inode; 	befs_debug(sb, "---> %s inode = %lu", __func__, ino); 	inode = iget_locked(sb, ino);	if (!inode)		return ERR_PTR(-ENOMEM);	if (!(inode_state_read_once(inode) & I_NEW))		return inode; 	befs_ino = BEFS_I(inode); 	/* convert from vfs's inode number to befs's inode number */	befs_ino->i_inode_num = blockno2iaddr(sb, inode->i_ino); 	befs_debug(sb, "  real inode number [%u, %hu, %hu]",		   befs_ino->i_inode_num.allocation_group,		   befs_ino->i_inode_num.start, befs_ino->i_inode_num.len); 	bh = sb_bread(sb, inode->i_ino);	if (!bh) {		befs_error(sb, "unable to read inode block - "			   "inode = %llu", inode->i_ino);		goto unacquire_none;	} 	raw_inode = (befs_inode *) bh->b_data; 	befs_dump_inode(sb, raw_inode); 	if (befs_check_inode(sb, raw_inode, inode->i_ino) != BEFS_OK) {		befs_error(sb, "Bad inode: %llu", inode->i_ino);		goto unacquire_bh;	} 	inode->i_mode = (umode_t) fs32_to_cpu(sb, raw_inode->mode); 	/*	 * set uid and gid.  But since current BeOS is single user OS, so	 * you can change by "uid" or "gid" options.	 */ 	inode->i_uid = befs_sb->mount_opts.use_uid ?		befs_sb->mount_opts.uid :		make_kuid(&init_user_ns, fs32_to_cpu(sb, raw_inode->uid));	inode->i_gid = befs_sb->mount_opts.use_gid ?		befs_sb->mount_opts.gid :		make_kgid(&init_user_ns, fs32_to_cpu(sb, raw_inode->gid)); 	set_nlink(inode, 1); 	/*	 * BEFS's time is 64 bits, but current VFS is 32 bits...	 * BEFS don't have access time. Nor inode change time. VFS	 * doesn't have creation time.	 * Also, the lower 16 bits of the last_modified_time and	 * create_time are just a counter to help ensure uniqueness	 * for indexing purposes. (PFD, page 54)	 */ 	inode_set_mtime(inode,			fs64_to_cpu(sb, raw_inode->last_modified_time) >> 16,			0);/* lower 16 bits are not a time */	inode_set_ctime_to_ts(inode, inode_get_mtime(inode));	inode_set_atime_to_ts(inode, inode_get_mtime(inode)); 	befs_ino->i_inode_num = fsrun_to_cpu(sb, raw_inode->inode_num);	befs_ino->i_parent = fsrun_to_cpu(sb, raw_inode->parent);	befs_ino->i_attribute = fsrun_to_cpu(sb, raw_inode->attributes);	befs_ino->i_flags = fs32_to_cpu(sb, raw_inode->flags); 	if (S_ISLNK(inode->i_mode) && !(befs_ino->i_flags & BEFS_LONG_SYMLINK)){		inode->i_size = 0;		inode->i_blocks = befs_sb->block_size / VFS_BLOCK_SIZE;		strscpy(befs_ino->i_data.symlink, raw_inode->data.symlink,			BEFS_SYMLINK_LEN);	} else {		int num_blks; 		befs_ino->i_data.ds =		    fsds_to_cpu(sb, &raw_inode->data.datastream); 		num_blks = befs_count_blocks(sb, &befs_ino->i_data.ds);		inode->i_blocks =		    num_blks * (befs_sb->block_size / VFS_BLOCK_SIZE);		inode->i_size = befs_ino->i_data.ds.size;	} 	inode->i_mapping->a_ops = &befs_aops; 	if (S_ISREG(inode->i_mode)) {		inode->i_fop = &generic_ro_fops;	} else if (S_ISDIR(inode->i_mode)) {		inode->i_op = &befs_dir_inode_operations;		inode->i_fop = &befs_dir_operations;	} else if (S_ISLNK(inode->i_mode)) {		if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {			inode->i_op = &page_symlink_inode_operations;			inode_nohighmem(inode);			inode->i_mapping->a_ops = &befs_symlink_aops;		} else {			inode->i_link = befs_ino->i_data.symlink;			inode->i_op = &simple_symlink_inode_operations;		}	} else {		befs_error(sb, "Inode %llu is not a regular file, "			   "directory or symlink. THAT IS WRONG! BeFS has no "			   "on disk special files", inode->i_ino);		goto unacquire_bh;	} 	brelse(bh);	befs_debug(sb, "<--- %s", __func__);	unlock_new_inode(inode);	return inode; unacquire_bh:	brelse(bh); unacquire_none:	iget_failed(inode);	befs_debug(sb, "<--- %s - Bad inode", __func__);	return ERR_PTR(-EIO);} /* Initialize the inode cache. Called at fs setup. * * Taken from NFS implementation by Al Viro. */static int __initbefs_init_inodecache(void){	befs_inode_cachep = kmem_cache_create_usercopy("befs_inode_cache",				sizeof(struct befs_inode_info), 0,				SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT,				offsetof(struct befs_inode_info,					i_data.symlink),				sizeof_field(struct befs_inode_info,					i_data.symlink),				init_once);	if (befs_inode_cachep == NULL)		return -ENOMEM; 	return 0;} /* Called at fs teardown. * * Taken from NFS implementation by Al Viro. */static voidbefs_destroy_inodecache(void){	/*	 * Make sure all delayed rcu free inodes are flushed before we	 * destroy cache.	 */	rcu_barrier();	kmem_cache_destroy(befs_inode_cachep);} /* * The inode of symbolic link is different to data stream. * The data stream become link name. Unless the LONG_SYMLINK * flag is set. */static int befs_symlink_read_folio(struct file *unused, struct folio *folio){	struct inode *inode = folio->mapping->host;	struct super_block *sb = inode->i_sb;	struct befs_inode_info *befs_ino = BEFS_I(inode);	befs_data_stream *data = &befs_ino->i_data.ds;	befs_off_t len = data->size;	char *link = folio_address(folio);	int err = -EIO; 	if (len == 0 || len > PAGE_SIZE) {		befs_error(sb, "Long symlink with illegal length");		goto fail;	}	befs_debug(sb, "Follow long symlink"); 	if (befs_read_lsymlink(sb, data, link, len) != len) {		befs_error(sb, "Failed to read entire long symlink");		goto fail;	}	link[len - 1] = '\0';	err = 0;fail:	folio_end_read(folio, err == 0);	return err;} /* * UTF-8 to NLS charset convert routine *

Lines 1–500 of 1030.