⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 amigaffs.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
字号:
/* *  linux/fs/affs/amigaffs.c * *  (c) 1996  Hans-Joachim Widmaier - Rewritten * *  (C) 1993  Ray Burr - Amiga FFS filesystem. * *  Please send bug reports to: hjw@zvw.de */#define DEBUG 0#include <stdarg.h>#include <linux/stat.h>#include <linux/sched.h>#include <linux/affs_fs.h>#include <linux/string.h>#include <linux/locks.h>#include <linux/mm.h>#include <linux/amigaffs.h>extern struct timezone sys_tz;static char ErrorBuffer[256];/* * Functions for accessing Amiga-FFS structures. *//* Set *NAME to point to the file name in a file header block in memory   pointed to by FH_DATA.  The length of the name is returned. */intaffs_get_file_name(int bsize, void *fh_data, unsigned char **name){	struct file_end *file_end;	file_end = GET_END_PTR(struct file_end, fh_data, bsize);	if (file_end->file_name[0] == 0	    || file_end->file_name[0] > 30) {		printk(KERN_WARNING "AFFS: bad filename (length=%d chars)\n",			file_end->file_name[0]);		*name = "***BAD_FILE***";		return 14;        }	*name = (unsigned char *)&file_end->file_name[1];        return file_end->file_name[0];}/* Insert a header block (file) into the directory (next). * This routine assumes that the caller has the superblock locked. */intaffs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode){	struct buffer_head	*bh;	s32			 ino;	int			 offset;	offset = affs_hash_name(FILE_END(file->b_data,inode)->file_name+1,				FILE_END(file->b_data,inode)->file_name[0],				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;	ino    = be32_to_cpu(((struct dir_front *)file->b_data)->own_key);	pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);	FILE_END(file->b_data,inode)->parent = cpu_to_be32(next);	while (1) {		if (!(bh = affs_bread(inode->i_dev,next,AFFS_I2BSIZE(inode))))			return -EIO;		next = be32_to_cpu(((s32 *)bh->b_data)[offset]);		if (!next || next > ino)			break;		offset = AFFS_I2BSIZE(inode) / 4 - 4;		affs_brelse(bh);	}	DIR_END(file->b_data,inode)->hash_chain = cpu_to_be32(next);	((s32 *)bh->b_data)[offset]             = cpu_to_be32(ino);	affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);	mark_buffer_dirty(bh);	affs_brelse(bh);	return 0;}/* Remove a header block from its hash table (directory). * 'inode' may be any inode on the partition, it's only * used for calculating the block size and superblock * reference. */intaffs_remove_hash(struct buffer_head *dbh, struct inode *inode){	s32			 ownkey;	s32			 key;	s32			 ptype;	s32			 stype;	int			 offset;	int			 retval;	struct buffer_head	*bh;	ownkey = be32_to_cpu(((struct dir_front *)dbh->b_data)->own_key);	key    = be32_to_cpu(FILE_END(dbh->b_data,inode)->parent);	offset = affs_hash_name(FILE_END(dbh->b_data,inode)->file_name+1,				FILE_END(dbh->b_data,inode)->file_name[0],				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;	pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n",key,ownkey,offset-6);	retval = -ENOENT;	lock_super(inode->i_sb);	while (key) {		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {			retval = -EIO;			break;		}		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)		    || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&					    stype != ST_LINKFILE && stype != ST_LINKDIR &&					    stype != ST_ROOT && stype != ST_SOFTLINK)) {			affs_error(inode->i_sb,"affs_remove_hash",				"Bad block in hash chain (key=%d, ptype=%d, stype=%d, ownkey=%d)",				key,ptype,stype,ownkey);			affs_brelse(bh);			retval = -EINVAL;			break;		}		key = be32_to_cpu(((s32 *)bh->b_data)[offset]);		if (ownkey == key) {			((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain;			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);			mark_buffer_dirty(bh);			affs_brelse(bh);			retval = 0;			break;		}		affs_brelse(bh);		offset = AFFS_I2BSIZE(inode) / 4 - 4;	}	unlock_super(inode->i_sb);	return retval;}/* Remove header from link chain */intaffs_remove_link(struct buffer_head *dbh, struct inode *inode){	int			 retval;	s32			 key;	s32			 ownkey;	s32			 ptype;	s32			 stype;	struct buffer_head	*bh;	ownkey = be32_to_cpu((DIR_FRONT(dbh)->own_key));	key    = be32_to_cpu(FILE_END(dbh->b_data,inode)->original);	retval = -ENOENT;	pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);	lock_super(inode->i_sb);	while (key) {		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {			retval = -EIO;			break;		}		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)) {			affs_error(inode->i_sb,"affs_remove_link","Checksum error (block %d)",key);			affs_brelse(bh);			retval = -EINVAL;			break;		}		key = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);		if (ownkey == key) {			FILE_END(bh->b_data,inode)->link_chain =						FILE_END(dbh->b_data,inode)->link_chain;			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);			mark_buffer_dirty(bh);			affs_brelse(bh);			retval = 0;			break;		}		affs_brelse(bh);	}	unlock_super(inode->i_sb);	return retval;}/* Remove a filesystem object. If the object to be removed has * links to it, one of the links must be changed to inherit * the file or directory. As above, any inode will do. * The buffer will not be freed. If the header is a link, the * block will be marked as free. * This function returns a negative error number in case of * an error, else 0 if the inode is to be deleted or 1 if not. */intaffs_remove_header(struct buffer_head *bh, struct inode *inode){	struct buffer_head	*link_bh;	struct inode		*dir;	unsigned long		 link_ino;	unsigned long		 orig_ino;	unsigned int		 dir_ino;	int			 error;	pr_debug("AFFS: remove_header(key=%ld)\n",be32_to_cpu(DIR_FRONT(bh)->own_key));	/* Mark directory as changed.  We do this before anything else,	 * as it must be done anyway and doesn't hurt even if an	 * error occurs later.	 */	dir = iget(inode->i_sb,be32_to_cpu(FILE_END(bh->b_data,inode)->parent));	if (!dir)		return -EIO;	dir->i_ctime = dir->i_mtime = CURRENT_TIME;	dir->i_version++;	mark_inode_dirty(dir);	iput(dir);	orig_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->original);	if (orig_ino) {		/* This is just a link. Nothing much to do. */		pr_debug("AFFS: Removing link.\n");		if ((error = affs_remove_link(bh,inode)))			return error;		if ((error = affs_remove_hash(bh,inode)))			return error;		affs_free_block(inode->i_sb,be32_to_cpu(DIR_FRONT(bh)->own_key));		return 1;	}		link_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);	if (link_ino) {		/* This is the complicated case. Yuck. */		pr_debug("AFFS: Removing original with links to it.\n");		/* Unlink the object and its first link from their directories. */		if ((error = affs_remove_hash(bh,inode)))			return error;		if (!(link_bh = affs_bread(inode->i_dev,link_ino,AFFS_I2BSIZE(inode))))			return -EIO;		if ((error = affs_remove_hash(link_bh,inode))) {			affs_brelse(link_bh);			return error;		}		/* Fix link chain. */		if ((error = affs_remove_link(link_bh,inode))) {			affs_brelse(link_bh);			return error;		}		/* Rename link to object. */		memcpy(FILE_END(bh->b_data,inode)->file_name,			FILE_END(link_bh->b_data,inode)->file_name,32);		/* Insert object into dir the link was in. */		dir_ino = be32_to_cpu(FILE_END(link_bh->b_data,inode)->parent);		if ((error = affs_insert_hash(dir_ino,bh,inode))) {			affs_brelse(link_bh);			return error;		}		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);		mark_buffer_dirty(bh);		affs_brelse(link_bh);		affs_free_block(inode->i_sb,link_ino);		/* Mark the link's parent dir as changed, too. */		if (!(dir = iget(inode->i_sb,dir_ino)))			return -EIO;		dir->i_ctime = dir->i_mtime = CURRENT_TIME;		dir->i_version++;		mark_inode_dirty(dir);		iput(dir);		return 1;	}	/* Plain file/dir. This is the simplest case. */	pr_debug("AFFS: Removing plain file/dir.\n");	if ((error = affs_remove_hash(bh,inode)))		return error;	return 0;}/* Checksum a block, do various consistency checks and optionally return   the blocks type number.  DATA points to the block.  If their pointers   are non-null, *PTYPE and *STYPE are set to the primary and secondary   block types respectively, *HASHSIZE is set to the size of the hashtable   (which lets us calculate the block size).   Returns non-zero if the block is not consistent. */u32affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype){	u32	 sum;	u32	*p;	bsize /= 4;	if (ptype)		*ptype = be32_to_cpu(((s32 *)data)[0]);	if (stype)		*stype = be32_to_cpu(((s32 *)data)[bsize - 1]);	sum    = 0;	p      = data;	while (bsize--)		sum += be32_to_cpu(*p++);	return sum;}/* * Calculate the checksum of a disk block and store it * at the indicated position. */voidaffs_fix_checksum(int bsize, void *data, int cspos){	u32	 ocs;	u32	 cs;	cs   = affs_checksum_block(bsize,data,NULL,NULL);	ocs  = be32_to_cpu(((u32 *)data)[cspos]);	ocs -= cs;	((u32 *)data)[cspos] = be32_to_cpu(ocs);}voidsecs_to_datestamp(time_t secs, struct DateStamp *ds){	u32	 days;	u32	 minute;	secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);	if (secs < 0)		secs = 0;	days    = secs / 86400;	secs   -= days * 86400;	minute  = secs / 60;	secs   -= minute * 60;	ds->ds_Days   = be32_to_cpu(days);	ds->ds_Minute = be32_to_cpu(minute);	ds->ds_Tick   = be32_to_cpu(secs * 50);}intprot_to_mode(u32 prot){	int	 mode = 0;	if (AFFS_UMAYWRITE(prot))		mode |= S_IWUSR;	if (AFFS_UMAYREAD(prot))		mode |= S_IRUSR;	if (AFFS_UMAYEXECUTE(prot))		mode |= S_IXUSR;	if (AFFS_GMAYWRITE(prot))		mode |= S_IWGRP;	if (AFFS_GMAYREAD(prot))		mode |= S_IRGRP;	if (AFFS_GMAYEXECUTE(prot))		mode |= S_IXGRP;	if (AFFS_OMAYWRITE(prot))		mode |= S_IWOTH;	if (AFFS_OMAYREAD(prot))		mode |= S_IROTH;	if (AFFS_OMAYEXECUTE(prot))		mode |= S_IXOTH;		return mode;}u32mode_to_prot(int mode){	u32	 prot = 0;	if (mode & S_IXUSR)		prot |= FIBF_SCRIPT;	if (mode & S_IRUSR)		prot |= FIBF_READ;	if (mode & S_IWUSR)		prot |= FIBF_WRITE | FIBF_DELETE;	if (mode & S_IRGRP)		prot |= FIBF_GRP_READ;	if (mode & S_IWGRP)		prot |= FIBF_GRP_WRITE;	if (mode & S_IROTH)		prot |= FIBF_OTR_READ;	if (mode & S_IWOTH)		prot |= FIBF_OTR_WRITE;		return prot;}voidaffs_error(struct super_block *sb, const char *function, const char *fmt, ...){	va_list	 args;	va_start(args,fmt);	vsprintf(ErrorBuffer,fmt,args);	va_end(args);	printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n",kdevname(sb->s_dev),		function,ErrorBuffer);	if (!(sb->s_flags & MS_RDONLY))		printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");	sb->s_flags |= MS_RDONLY;	sb->u.affs_sb.s_flags |= SF_READONLY;	/* Don't allow to remount rw */}voidaffs_warning(struct super_block *sb, const char *function, const char *fmt, ...){	va_list	 args;	va_start(args,fmt);	vsprintf(ErrorBuffer,fmt,args);	va_end(args);	printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),		function,ErrorBuffer);}/* Check if the name is valid for a affs object. */intaffs_check_name(const unsigned char *name, int len){	int	 i;	if (len > 30)#ifdef AFFS_NO_TRUNCATE		return -ENAMETOOLONG;#else		len = 30;#endif	for (i = 0; i < len; i++) {		if (name[i] < ' ' || name[i] == ':'		    || (name[i] > 0x7e && name[i] < 0xa0))			return -EINVAL;	}	return 0;}/* This function copies name to bstr, with at most 30 * characters length. The bstr will be prepended by * a length byte. * NOTE: The name will must be already checked by *       affs_check_name()! */intaffs_copy_name(unsigned char *bstr, const unsigned char *name){	int	 len;	for (len = 0; len < 30; len++) {		bstr[len + 1] = name[len];		if (name[len] == '\0')			break;	}	bstr[0] = len;	return len;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -