brintos

brintos / linux-shallow public Read only

0
0
Text · 13.9 KiB · 06ac5a7 Raw
557 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2000-2005 Silicon Graphics, Inc.4 * Copyright (c) 2013 Red Hat, Inc.5 * All Rights Reserved.6 */7#include "xfs.h"8#include "xfs_fs.h"9#include "xfs_shared.h"10#include "xfs_format.h"11#include "xfs_log_format.h"12#include "xfs_trans_resv.h"13#include "xfs_mount.h"14#include "xfs_inode.h"15#include "xfs_dir2.h"16#include "xfs_dir2_priv.h"17#include "xfs_trace.h"18#include "xfs_bmap.h"19#include "xfs_trans.h"20#include "xfs_error.h"21#include "xfs_health.h"22 23/*24 * Directory file type support functions25 */26static unsigned char xfs_dir3_filetype_table[] = {27	DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK,28	DT_FIFO, DT_SOCK, DT_LNK, DT_WHT,29};30 31unsigned char32xfs_dir3_get_dtype(33	struct xfs_mount	*mp,34	uint8_t			filetype)35{36	if (!xfs_has_ftype(mp))37		return DT_UNKNOWN;38 39	if (filetype >= XFS_DIR3_FT_MAX)40		return DT_UNKNOWN;41 42	return xfs_dir3_filetype_table[filetype];43}44 45STATIC int46xfs_dir2_sf_getdents(47	struct xfs_da_args	*args,48	struct dir_context	*ctx)49{50	int			i;		/* shortform entry number */51	struct xfs_inode	*dp = args->dp;	/* incore directory inode */52	struct xfs_mount	*mp = dp->i_mount;53	xfs_dir2_dataptr_t	off;		/* current entry's offset */54	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */55	struct xfs_dir2_sf_hdr	*sfp = dp->i_df.if_data;56	xfs_dir2_dataptr_t	dot_offset;57	xfs_dir2_dataptr_t	dotdot_offset;58	xfs_ino_t		ino;59	struct xfs_da_geometry	*geo = args->geo;60 61	ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL);62	ASSERT(dp->i_df.if_bytes == dp->i_disk_size);63	ASSERT(sfp != NULL);64 65	/*66	 * If the block number in the offset is out of range, we're done.67	 */68	if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)69		return 0;70 71	/*72	 * Precalculate offsets for "." and ".." as we will always need them.73	 * This relies on the fact that directories always start with the74	 * entries for "." and "..".75	 */76	dot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,77			geo->data_entry_offset);78	dotdot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,79			geo->data_entry_offset +80			xfs_dir2_data_entsize(mp, sizeof(".") - 1));81 82	/*83	 * Put . entry unless we're starting past it.84	 */85	if (ctx->pos <= dot_offset) {86		ctx->pos = dot_offset & 0x7fffffff;87		if (!dir_emit(ctx, ".", 1, dp->i_ino, DT_DIR))88			return 0;89	}90 91	/*92	 * Put .. entry unless we're starting past it.93	 */94	if (ctx->pos <= dotdot_offset) {95		ino = xfs_dir2_sf_get_parent_ino(sfp);96		ctx->pos = dotdot_offset & 0x7fffffff;97		if (!dir_emit(ctx, "..", 2, ino, DT_DIR))98			return 0;99	}100 101	/*102	 * Loop while there are more entries and put'ing works.103	 */104	sfep = xfs_dir2_sf_firstentry(sfp);105	for (i = 0; i < sfp->count; i++) {106		uint8_t filetype;107 108		off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,109				xfs_dir2_sf_get_offset(sfep));110 111		if (ctx->pos > off) {112			sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);113			continue;114		}115 116		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);117		filetype = xfs_dir2_sf_get_ftype(mp, sfep);118		ctx->pos = off & 0x7fffffff;119		if (XFS_IS_CORRUPT(dp->i_mount,120				   !xfs_dir2_namecheck(sfep->name,121						       sfep->namelen))) {122			xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);123			return -EFSCORRUPTED;124		}125		if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino,126			    xfs_dir3_get_dtype(mp, filetype)))127			return 0;128		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);129	}130 131	ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &132								0x7fffffff;133	return 0;134}135 136/*137 * Readdir for block directories.138 */139STATIC int140xfs_dir2_block_getdents(141	struct xfs_da_args	*args,142	struct dir_context	*ctx,143	unsigned int		*lock_mode)144{145	struct xfs_inode	*dp = args->dp;	/* incore directory inode */146	struct xfs_buf		*bp;		/* buffer for block */147	int			error;		/* error return value */148	int			wantoff;	/* starting block offset */149	xfs_off_t		cook;150	struct xfs_da_geometry	*geo = args->geo;151	unsigned int		offset, next_offset;152	unsigned int		end;153 154	/*155	 * If the block number in the offset is out of range, we're done.156	 */157	if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)158		return 0;159 160	error = xfs_dir3_block_read(args->trans, dp, args->owner, &bp);161	if (error)162		return error;163 164	xfs_iunlock(dp, *lock_mode);165	*lock_mode = 0;166 167	/*168	 * Extract the byte offset we start at from the seek pointer.169	 * We'll skip entries before this.170	 */171	wantoff = xfs_dir2_dataptr_to_off(geo, ctx->pos);172	xfs_dir3_data_check(dp, bp);173 174	/*175	 * Loop over the data portion of the block.176	 * Each object is a real entry (dep) or an unused one (dup).177	 */178	end = xfs_dir3_data_end_offset(geo, bp->b_addr);179	for (offset = geo->data_entry_offset;180	     offset < end;181	     offset = next_offset) {182		struct xfs_dir2_data_unused	*dup = bp->b_addr + offset;183		struct xfs_dir2_data_entry	*dep = bp->b_addr + offset;184		uint8_t filetype;185 186		/*187		 * Unused, skip it.188		 */189		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {190			next_offset = offset + be16_to_cpu(dup->length);191			continue;192		}193 194		/*195		 * Bump pointer for the next iteration.196		 */197		next_offset = offset +198			xfs_dir2_data_entsize(dp->i_mount, dep->namelen);199 200		/*201		 * The entry is before the desired starting point, skip it.202		 */203		if (offset < wantoff)204			continue;205 206		cook = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, offset);207 208		ctx->pos = cook & 0x7fffffff;209		filetype = xfs_dir2_data_get_ftype(dp->i_mount, dep);210		/*211		 * If it didn't fit, set the final offset to here & return.212		 */213		if (XFS_IS_CORRUPT(dp->i_mount,214				   !xfs_dir2_namecheck(dep->name,215						       dep->namelen))) {216			xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);217			error = -EFSCORRUPTED;218			goto out_rele;219		}220		if (!dir_emit(ctx, (char *)dep->name, dep->namelen,221			    be64_to_cpu(dep->inumber),222			    xfs_dir3_get_dtype(dp->i_mount, filetype)))223			goto out_rele;224	}225 226	/*227	 * Reached the end of the block.228	 * Set the offset to a non-existent block 1 and return.229	 */230	ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &231								0x7fffffff;232out_rele:233	xfs_trans_brelse(args->trans, bp);234	return error;235}236 237/*238 * Read a directory block and initiate readahead for blocks beyond that.239 * We maintain a sliding readahead window of the remaining space in the240 * buffer rounded up to the nearest block.241 */242STATIC int243xfs_dir2_leaf_readbuf(244	struct xfs_da_args	*args,245	size_t			bufsize,246	xfs_dir2_off_t		*cur_off,247	xfs_dablk_t		*ra_blk,248	struct xfs_buf		**bpp)249{250	struct xfs_inode	*dp = args->dp;251	struct xfs_buf		*bp = NULL;252	struct xfs_da_geometry	*geo = args->geo;253	struct xfs_ifork	*ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);254	struct xfs_bmbt_irec	map;255	struct blk_plug		plug;256	xfs_dir2_off_t		new_off;257	xfs_dablk_t		next_ra;258	xfs_dablk_t		map_off;259	xfs_dablk_t		last_da;260	struct xfs_iext_cursor	icur;261	int			ra_want;262	int			error = 0;263 264	error = xfs_iread_extents(args->trans, dp, XFS_DATA_FORK);265	if (error)266		goto out;267 268	/*269	 * Look for mapped directory blocks at or above the current offset.270	 * Truncate down to the nearest directory block to start the scanning271	 * operation.272	 */273	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);274	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *cur_off));275	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))276		goto out;277	if (map.br_startoff >= last_da)278		goto out;279	xfs_trim_extent(&map, map_off, last_da - map_off);280 281	/* Read the directory block of that first mapping. */282	new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);283	if (new_off > *cur_off)284		*cur_off = new_off;285	error = xfs_dir3_data_read(args->trans, dp, args->owner,286			map.br_startoff, 0, &bp);287	if (error)288		goto out;289 290	/*291	 * Start readahead for the next bufsize's worth of dir data blocks.292	 * We may have already issued readahead for some of that range;293	 * ra_blk tracks the last block we tried to read(ahead).294	 */295	ra_want = howmany(bufsize + geo->blksize, (1 << geo->fsblog));296	if (*ra_blk >= last_da)297		goto out;298	else if (*ra_blk == 0)299		*ra_blk = map.br_startoff;300	next_ra = map.br_startoff + geo->fsbcount;301	if (next_ra >= last_da)302		goto out_no_ra;303	if (map.br_blockcount < geo->fsbcount &&304	    !xfs_iext_next_extent(ifp, &icur, &map))305		goto out_no_ra;306	if (map.br_startoff >= last_da)307		goto out_no_ra;308	xfs_trim_extent(&map, next_ra, last_da - next_ra);309 310	/* Start ra for each dir (not fs) block that has a mapping. */311	blk_start_plug(&plug);312	while (ra_want > 0) {313		next_ra = roundup((xfs_dablk_t)map.br_startoff, geo->fsbcount);314		while (ra_want > 0 &&315		       next_ra < map.br_startoff + map.br_blockcount) {316			if (next_ra >= last_da) {317				*ra_blk = last_da;318				break;319			}320			if (next_ra > *ra_blk) {321				xfs_dir3_data_readahead(dp, next_ra,322							XFS_DABUF_MAP_HOLE_OK);323				*ra_blk = next_ra;324			}325			ra_want -= geo->fsbcount;326			next_ra += geo->fsbcount;327		}328		if (!xfs_iext_next_extent(ifp, &icur, &map)) {329			*ra_blk = last_da;330			break;331		}332	}333	blk_finish_plug(&plug);334 335out:336	*bpp = bp;337	return error;338out_no_ra:339	*ra_blk = last_da;340	goto out;341}342 343/*344 * Getdents (readdir) for leaf and node directories.345 * This reads the data blocks only, so is the same for both forms.346 */347STATIC int348xfs_dir2_leaf_getdents(349	struct xfs_da_args	*args,350	struct dir_context	*ctx,351	size_t			bufsize,352	unsigned int		*lock_mode)353{354	struct xfs_inode	*dp = args->dp;355	struct xfs_mount	*mp = dp->i_mount;356	struct xfs_buf		*bp = NULL;	/* data block buffer */357	xfs_dir2_data_entry_t	*dep;		/* data entry */358	xfs_dir2_data_unused_t	*dup;		/* unused entry */359	struct xfs_da_geometry	*geo = args->geo;360	xfs_dablk_t		rablk = 0;	/* current readahead block */361	xfs_dir2_off_t		curoff;		/* current overall offset */362	int			length;		/* temporary length value */363	int			byteoff;	/* offset in current block */364	unsigned int		offset = 0;365	int			error = 0;	/* error return value */366 367	/*368	 * If the offset is at or past the largest allowed value,369	 * give up right away.370	 */371	if (ctx->pos >= XFS_DIR2_MAX_DATAPTR)372		return 0;373 374	/*375	 * Inside the loop we keep the main offset value as a byte offset376	 * in the directory file.377	 */378	curoff = xfs_dir2_dataptr_to_byte(ctx->pos);379 380	/*381	 * Loop over directory entries until we reach the end offset.382	 * Get more blocks and readahead as necessary.383	 */384	while (curoff < XFS_DIR2_LEAF_OFFSET) {385		uint8_t filetype;386 387		/*388		 * If we have no buffer, or we're off the end of the389		 * current buffer, need to get another one.390		 */391		if (!bp || offset >= geo->blksize) {392			if (bp) {393				xfs_trans_brelse(args->trans, bp);394				bp = NULL;395			}396 397			if (*lock_mode == 0)398				*lock_mode = xfs_ilock_data_map_shared(dp);399			error = xfs_dir2_leaf_readbuf(args, bufsize, &curoff,400					&rablk, &bp);401			if (error || !bp)402				break;403 404			xfs_iunlock(dp, *lock_mode);405			*lock_mode = 0;406 407			xfs_dir3_data_check(dp, bp);408			/*409			 * Find our position in the block.410			 */411			offset = geo->data_entry_offset;412			byteoff = xfs_dir2_byte_to_off(geo, curoff);413			/*414			 * Skip past the header.415			 */416			if (byteoff == 0)417				curoff += geo->data_entry_offset;418			/*419			 * Skip past entries until we reach our offset.420			 */421			else {422				while (offset < byteoff) {423					dup = bp->b_addr + offset;424 425					if (be16_to_cpu(dup->freetag)426						  == XFS_DIR2_DATA_FREE_TAG) {427 428						length = be16_to_cpu(dup->length);429						offset += length;430						continue;431					}432					dep = bp->b_addr + offset;433					length = xfs_dir2_data_entsize(mp,434							dep->namelen);435					offset += length;436				}437				/*438				 * Now set our real offset.439				 */440				curoff =441					xfs_dir2_db_off_to_byte(geo,442					    xfs_dir2_byte_to_db(geo, curoff),443					    offset);444				if (offset >= geo->blksize)445					continue;446			}447		}448 449		/*450		 * We have a pointer to an entry.  Is it a live one?451		 */452		dup = bp->b_addr + offset;453 454		/*455		 * No, it's unused, skip over it.456		 */457		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {458			length = be16_to_cpu(dup->length);459			offset += length;460			curoff += length;461			continue;462		}463 464		dep = bp->b_addr + offset;465		length = xfs_dir2_data_entsize(mp, dep->namelen);466		filetype = xfs_dir2_data_get_ftype(mp, dep);467 468		ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;469		if (XFS_IS_CORRUPT(dp->i_mount,470				   !xfs_dir2_namecheck(dep->name,471						       dep->namelen))) {472			xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);473			error = -EFSCORRUPTED;474			break;475		}476		if (!dir_emit(ctx, (char *)dep->name, dep->namelen,477			    be64_to_cpu(dep->inumber),478			    xfs_dir3_get_dtype(dp->i_mount, filetype)))479			break;480 481		/*482		 * Advance to next entry in the block.483		 */484		offset += length;485		curoff += length;486		/* bufsize may have just been a guess; don't go negative */487		bufsize = bufsize > length ? bufsize - length : 0;488	}489 490	/*491	 * All done.  Set output offset value to current offset.492	 */493	if (curoff > xfs_dir2_dataptr_to_byte(XFS_DIR2_MAX_DATAPTR))494		ctx->pos = XFS_DIR2_MAX_DATAPTR & 0x7fffffff;495	else496		ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;497	if (bp)498		xfs_trans_brelse(args->trans, bp);499	return error;500}501 502/*503 * Read a directory.504 *505 * If supplied, the transaction collects locked dir buffers to avoid506 * nested buffer deadlocks.  This function does not dirty the507 * transaction.  The caller must hold the IOLOCK (shared or exclusive)508 * before calling this function.509 */510int511xfs_readdir(512	struct xfs_trans	*tp,513	struct xfs_inode	*dp,514	struct dir_context	*ctx,515	size_t			bufsize)516{517	struct xfs_da_args	args = { NULL };518	unsigned int		lock_mode;519	int			error;520 521	trace_xfs_readdir(dp);522 523	if (xfs_is_shutdown(dp->i_mount))524		return -EIO;525	if (xfs_ifork_zapped(dp, XFS_DATA_FORK))526		return -EIO;527 528	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));529	xfs_assert_ilocked(dp, XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL);530	XFS_STATS_INC(dp->i_mount, xs_dir_getdents);531 532	args.dp = dp;533	args.geo = dp->i_mount->m_dir_geo;534	args.trans = tp;535	args.owner = dp->i_ino;536 537	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)538		return xfs_dir2_sf_getdents(&args, ctx);539 540	lock_mode = xfs_ilock_data_map_shared(dp);541	switch (xfs_dir2_format(&args, &error)) {542	case XFS_DIR2_FMT_BLOCK:543		error = xfs_dir2_block_getdents(&args, ctx, &lock_mode);544		break;545	case XFS_DIR2_FMT_LEAF:546	case XFS_DIR2_FMT_NODE:547		error = xfs_dir2_leaf_getdents(&args, ctx, bufsize, &lock_mode);548		break;549	default:550		break;551	}552 553	if (lock_mode)554		xfs_iunlock(dp, lock_mode);555	return error;556}557