xfs_itable.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 807 行 · 第 1/2 页

C
807
字号
/* * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like.  Any license provided herein, whether implied or * otherwise, applies only to this software file.  Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA  94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */#include "xfs.h"#include "xfs_macros.h"#include "xfs_types.h"#include "xfs_inum.h"#include "xfs_log.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_dir.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_ag.h"#include "xfs_alloc_btree.h"#include "xfs_bmap_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_btree.h"#include "xfs_attr_sf.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_ialloc.h"#include "xfs_itable.h"#include "xfs_error.h"#ifndef HAVE_USERACC#define useracc(ubuffer, size, flags, foo) (0)#define unuseracc(ubuffer, size, flags)#endif/* * Return stat information for one inode. * Return 0 if ok, else errno. */int		       		/* error status */xfs_bulkstat_one(	xfs_mount_t	*mp,		/* mount point for filesystem */	xfs_ino_t	ino,		/* inode number to get data for */	void		__user *buffer,	/* buffer to place output in */	int		ubsize,		/* size of buffer */	void		*private_data,	/* my private data */	xfs_daddr_t	bno,		/* starting bno of inode cluster */	int		*ubused,	/* bytes used by me */	void		*dibuff,	/* on-disk inode buffer */	int		*stat)		/* BULKSTAT_RV_... */{	xfs_bstat_t	*buf;		/* return buffer */	int		error;		/* error value */	xfs_dinode_t	*dip;		/* dinode inode pointer */	xfs_dinode_core_t *dic;		/* dinode core info pointer */	xfs_inode_t	*ip = NULL;	/* incore inode pointer */	xfs_arch_t      arch;           /* these are set according to      */	dip = (xfs_dinode_t *)dibuff;	if (!buffer || ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||	    (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&	     (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino))) {		*stat = BULKSTAT_RV_NOTHING;		return XFS_ERROR(EINVAL);	}	if (ubsize < sizeof(*buf)) {		*stat = BULKSTAT_RV_NOTHING;		return XFS_ERROR(ENOMEM);	}	buf = kmem_alloc(sizeof(*buf), KM_SLEEP);	if (dip == NULL) {		/* We're not being passed a pointer to a dinode.  This happens		 * if BULKSTAT_FG_IGET is selected.  Do the iget.		 */		error = xfs_iget(mp, NULL, ino, XFS_ILOCK_SHARED, &ip, bno);		if (error) {			*stat = BULKSTAT_RV_NOTHING;			return error;		}		ASSERT(ip != NULL);		ASSERT(ip->i_blkno != (xfs_daddr_t)0);		if (ip->i_d.di_mode == 0) {			xfs_iput_new(ip, XFS_ILOCK_SHARED);			*stat = BULKSTAT_RV_NOTHING;			kmem_free(buf, sizeof(*buf));			return XFS_ERROR(ENOENT);		}		dic = &ip->i_d;		arch = ARCH_NOCONVERT;		/* in-core! */		ASSERT(dic != NULL);		/* xfs_iget returns the following without needing		 * further change.		 */		buf->bs_nlink = dic->di_nlink;		buf->bs_projid = dic->di_projid;	} else {		dic = &dip->di_core;		ASSERT(dic != NULL);		/* buffer dinode_core is in on-disk arch */		arch = ARCH_CONVERT;		/*		 * The inode format changed when we moved the link count and		 * made it 32 bits long.  If this is an old format inode,		 * convert it in memory to look like a new one.  If it gets		 * flushed to disk we will convert back before flushing or		 * logging it.  We zero out the new projid field and the old link		 * count field.  We'll handle clearing the pad field (the remains		 * of the old uuid field) when we actually convert the inode to		 * the new format. We don't change the version number so that we		 * can distinguish this from a real new format inode.		 */		if (INT_GET(dic->di_version, arch) == XFS_DINODE_VERSION_1) {			buf->bs_nlink = INT_GET(dic->di_onlink, arch);			buf->bs_projid = 0;		}		else {			buf->bs_nlink = INT_GET(dic->di_nlink, arch);			buf->bs_projid = INT_GET(dic->di_projid, arch);		}	}	buf->bs_ino = ino;	buf->bs_mode = INT_GET(dic->di_mode, arch);	buf->bs_uid = INT_GET(dic->di_uid, arch);	buf->bs_gid = INT_GET(dic->di_gid, arch);	buf->bs_size = INT_GET(dic->di_size, arch);	buf->bs_atime.tv_sec = INT_GET(dic->di_atime.t_sec, arch);	buf->bs_atime.tv_nsec = INT_GET(dic->di_atime.t_nsec, arch);	buf->bs_mtime.tv_sec = INT_GET(dic->di_mtime.t_sec, arch);	buf->bs_mtime.tv_nsec = INT_GET(dic->di_mtime.t_nsec, arch);	buf->bs_ctime.tv_sec = INT_GET(dic->di_ctime.t_sec, arch);	buf->bs_ctime.tv_nsec = INT_GET(dic->di_ctime.t_nsec, arch);	buf->bs_xflags = xfs_dic2xflags(dic, arch);	buf->bs_extsize = INT_GET(dic->di_extsize, arch) << mp->m_sb.sb_blocklog;	buf->bs_extents = INT_GET(dic->di_nextents, arch);	buf->bs_gen = INT_GET(dic->di_gen, arch);	memset(buf->bs_pad, 0, sizeof(buf->bs_pad));	buf->bs_dmevmask = INT_GET(dic->di_dmevmask, arch);	buf->bs_dmstate = INT_GET(dic->di_dmstate, arch);	buf->bs_aextents = INT_GET(dic->di_anextents, arch);	switch (INT_GET(dic->di_format, arch)) {	case XFS_DINODE_FMT_DEV:		if ( ip ) {			buf->bs_rdev = ip->i_df.if_u2.if_rdev;		} else {			buf->bs_rdev = INT_GET(dip->di_u.di_dev, arch);		}		buf->bs_blksize = BLKDEV_IOSIZE;		buf->bs_blocks = 0;		break;	case XFS_DINODE_FMT_LOCAL:	case XFS_DINODE_FMT_UUID:		buf->bs_rdev = 0;		buf->bs_blksize = mp->m_sb.sb_blocksize;		buf->bs_blocks = 0;		break;	case XFS_DINODE_FMT_EXTENTS:	case XFS_DINODE_FMT_BTREE:		buf->bs_rdev = 0;		buf->bs_blksize = mp->m_sb.sb_blocksize;		if ( ip ) {			buf->bs_blocks = INT_GET(dic->di_nblocks, arch) + ip->i_delayed_blks;		} else {			buf->bs_blocks = INT_GET(dic->di_nblocks, arch);		}		break;	}	if (ip) {		xfs_iput(ip, XFS_ILOCK_SHARED);	}	if (copy_to_user(buffer, buf, sizeof(*buf)))  {		kmem_free(buf, sizeof(*buf));		*stat = BULKSTAT_RV_NOTHING;		return EFAULT;	}	kmem_free(buf, sizeof(*buf));	*stat = BULKSTAT_RV_DIDONE;	if (ubused)		*ubused = sizeof(*buf);	return 0;}/* * Return stat information in bulk (by-inode) for the filesystem. */int					/* error status */xfs_bulkstat(	xfs_mount_t		*mp,	/* mount point for filesystem */	xfs_ino_t		*lastinop, /* last inode returned */	int			*ubcountp, /* size of buffer/count returned */	bulkstat_one_pf		formatter, /* func that'd fill a single buf */	void			*private_data,/* private data for formatter */	size_t			statstruct_size, /* sizeof struct filling */	char			__user *ubuffer, /* buffer with inode stats */	int			flags,	/* defined in xfs_itable.h */	int			*done)	/* 1 if there're more stats to get */{	xfs_agblock_t		agbno=0;/* allocation group block number */	xfs_buf_t		*agbp;	/* agi header buffer */	xfs_agi_t		*agi;	/* agi header data */	xfs_agino_t		agino;	/* inode # in allocation group */	xfs_agnumber_t		agno;	/* allocation group number */	xfs_daddr_t		bno;	/* inode cluster start daddr */	int			chunkidx; /* current index into inode chunk */	int			clustidx; /* current index into inode cluster */	xfs_btree_cur_t		*cur;	/* btree cursor for ialloc btree */	int			end_of_ag; /* set if we've seen the ag end */	int			error;	/* error code */	int                     fmterror;/* bulkstat formatter result */	__int32_t		gcnt;	/* current btree rec's count */	xfs_inofree_t		gfree;	/* current btree rec's free mask */	xfs_agino_t		gino;	/* current btree rec's start inode */	int			i;	/* loop index */	int			icount;	/* count of inodes good in irbuf */	xfs_ino_t		ino;	/* inode number (filesystem) */	xfs_inobt_rec_t		*irbp;	/* current irec buffer pointer */	xfs_inobt_rec_t		*irbuf;	/* start of irec buffer */	xfs_inobt_rec_t		*irbufend; /* end of good irec buffer entries */	xfs_ino_t		lastino=0; /* last inode number returned */	int			nbcluster; /* # of blocks in a cluster */	int			nicluster; /* # of inodes in a cluster */	int			nimask;	/* mask for inode clusters */	int			nirbuf;	/* size of irbuf */	int			rval;	/* return value error code */	int			tmp;	/* result value from btree calls */	int			ubcount; /* size of user's buffer */	int			ubleft;	/* bytes left in user's buffer */	char			__user *ubufp;	/* pointer into user's buffer */	int			ubelem;	/* spaces used in user's buffer */	int			ubused;	/* bytes used by formatter */	xfs_buf_t		*bp;	/* ptr to on-disk inode cluster buf */	xfs_dinode_t		*dip;	/* ptr into bp for specific inode */	xfs_inode_t		*ip;	/* ptr to in-core inode struct */	/*	 * Get the last inode value, see if there's nothing to do.	 */	ino = (xfs_ino_t)*lastinop;	dip = NULL;	agno = XFS_INO_TO_AGNO(mp, ino);	agino = XFS_INO_TO_AGINO(mp, ino);	if (agno >= mp->m_sb.sb_agcount ||	    ino != XFS_AGINO_TO_INO(mp, agno, agino)) {		*done = 1;		*ubcountp = 0;		return 0;	}	ubcount = *ubcountp; /* statstruct's */	ubleft = ubcount * statstruct_size; /* bytes */	*ubcountp = ubelem = 0;	*done = 0;	fmterror = 0;	ubufp = ubuffer;	nicluster = mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp) ?		mp->m_sb.sb_inopblock :		(XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog);	nimask = ~(nicluster - 1);	nbcluster = nicluster >> mp->m_sb.sb_inopblog;	/*	 * Lock down the user's buffer. If a buffer was not sent, as in the case	 * disk quota code calls here, we skip this.	 */	if (ubuffer &&	    (error = useracc(ubuffer, ubcount * statstruct_size,			(B_READ|B_PHYS), NULL))) {		return error;	}	/*	 * Allocate a page-sized buffer for inode btree records.	 * We could try allocating something smaller, but for normal	 * calls we'll always (potentially) need the whole page.	 */	irbuf = kmem_alloc(NBPC, KM_SLEEP);	nirbuf = NBPC / sizeof(*irbuf);	/*	 * Loop over the allocation groups, starting from the last	 * inode returned; 0 means start of the allocation group.	 */	rval = 0;	while (ubleft >= statstruct_size && agno < mp->m_sb.sb_agcount) {		bp = NULL;		down_read(&mp->m_peraglock);		error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);		up_read(&mp->m_peraglock);		if (error) {			/*			 * Skip this allocation group and go to the next one.			 */			agno++;			agino = 0;			continue;		}		agi = XFS_BUF_TO_AGI(agbp);		/*		 * Allocate and initialize a btree cursor for ialloc btree.		 */		cur = xfs_btree_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_INO,			(xfs_inode_t *)0, 0);		irbp = irbuf;		irbufend = irbuf + nirbuf;		end_of_ag = 0;		/*		 * If we're returning in the middle of an allocation group,		 * we need to get the remainder of the chunk we're in.		 */		if (agino > 0) {			/*			 * Lookup the inode chunk that this inode lives in.			 */			error = xfs_inobt_lookup_le(cur, agino, 0, 0, &tmp);			if (!error &&	/* no I/O error */			    tmp &&	/* lookup succeeded */					/* got the record, should always work */			    !(error = xfs_inobt_get_rec(cur, &gino, &gcnt,				    &gfree, &i, ARCH_NOCONVERT)) &&			    i == 1 &&					/* this is the right chunk */			    agino < gino + XFS_INODES_PER_CHUNK &&					/* lastino was not last in chunk */			    (chunkidx = agino - gino + 1) <				    XFS_INODES_PER_CHUNK &&					/* there are some left allocated */			    XFS_INOBT_MASKN(chunkidx,				    XFS_INODES_PER_CHUNK - chunkidx) & ~gfree) {				/*				 * Grab the chunk record.  Mark all the				 * uninteresting inodes (because they're				 * before our start point) free.				 */				for (i = 0; i < chunkidx; i++) {					if (XFS_INOBT_MASK(i) & ~gfree)						gcnt++;				}				gfree |= XFS_INOBT_MASKN(0, chunkidx);				INT_SET(irbp->ir_startino, ARCH_CONVERT, gino);				INT_SET(irbp->ir_freecount, ARCH_CONVERT, gcnt);				INT_SET(irbp->ir_free, ARCH_CONVERT, gfree);				irbp++;				agino = gino + XFS_INODES_PER_CHUNK;				icount = XFS_INODES_PER_CHUNK - gcnt;			} else {				/*				 * If any of those tests failed, bump the				 * inode number (just in case).				 */				agino++;				icount = 0;			}			/*			 * In any case, increment to the next record.			 */			if (!error)				error = xfs_inobt_increment(cur, 0, &tmp);		} else {			/*			 * Start of ag.  Lookup the first inode chunk.			 */			error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &tmp);			icount = 0;		}		/*		 * Loop through inode btree records in this ag,		 * until we run out of inodes or space in the buffer.		 */

⌨️ 快捷键说明

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