Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
  3 * Copyright (c) 2013 Red Hat, Inc.
  4 * All Rights Reserved.
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it would be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 * GNU General Public License for more details.
 14 *
 15 * You should have received a copy of the GNU General Public License
 16 * along with this program; if not, write the Free Software Foundation,
 17 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 18 */
 19#include "xfs.h"
 20#include "xfs_fs.h"
 21#include "xfs_format.h"
 22#include "xfs_log_format.h"
 23#include "xfs_trans_resv.h"
 24#include "xfs_bit.h"
 25#include "xfs_sb.h"
 26#include "xfs_ag.h"
 27#include "xfs_mount.h"
 28#include "xfs_da_format.h"
 29#include "xfs_da_btree.h"
 30#include "xfs_inode.h"
 31#include "xfs_trans.h"
 32#include "xfs_inode_item.h"
 33#include "xfs_bmap.h"
 34#include "xfs_attr.h"
 35#include "xfs_attr_sf.h"
 36#include "xfs_attr_remote.h"
 37#include "xfs_attr_leaf.h"
 38#include "xfs_error.h"
 39#include "xfs_trace.h"
 40#include "xfs_buf_item.h"
 41#include "xfs_cksum.h"
 42#include "xfs_dinode.h"
 43#include "xfs_dir2.h"
 44
 45STATIC int
 46xfs_attr_shortform_compare(const void *a, const void *b)
 47{
 48	xfs_attr_sf_sort_t *sa, *sb;
 49
 50	sa = (xfs_attr_sf_sort_t *)a;
 51	sb = (xfs_attr_sf_sort_t *)b;
 52	if (sa->hash < sb->hash) {
 53		return(-1);
 54	} else if (sa->hash > sb->hash) {
 55		return(1);
 56	} else {
 57		return(sa->entno - sb->entno);
 58	}
 59}
 60
 61#define XFS_ISRESET_CURSOR(cursor) \
 62	(!((cursor)->initted) && !((cursor)->hashval) && \
 63	 !((cursor)->blkno) && !((cursor)->offset))
 64/*
 65 * Copy out entries of shortform attribute lists for attr_list().
 66 * Shortform attribute lists are not stored in hashval sorted order.
 67 * If the output buffer is not large enough to hold them all, then we
 68 * we have to calculate each entries' hashvalue and sort them before
 69 * we can begin returning them to the user.
 70 */
 71int
 72xfs_attr_shortform_list(xfs_attr_list_context_t *context)
 73{
 74	attrlist_cursor_kern_t *cursor;
 75	xfs_attr_sf_sort_t *sbuf, *sbp;
 76	xfs_attr_shortform_t *sf;
 77	xfs_attr_sf_entry_t *sfe;
 78	xfs_inode_t *dp;
 79	int sbsize, nsbuf, count, i;
 80	int error;
 81
 82	ASSERT(context != NULL);
 83	dp = context->dp;
 84	ASSERT(dp != NULL);
 85	ASSERT(dp->i_afp != NULL);
 86	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
 87	ASSERT(sf != NULL);
 88	if (!sf->hdr.count)
 89		return(0);
 90	cursor = context->cursor;
 91	ASSERT(cursor != NULL);
 92
 93	trace_xfs_attr_list_sf(context);
 94
 95	/*
 96	 * If the buffer is large enough and the cursor is at the start,
 97	 * do not bother with sorting since we will return everything in
 98	 * one buffer and another call using the cursor won't need to be
 99	 * made.
100	 * Note the generous fudge factor of 16 overhead bytes per entry.
101	 * If bufsize is zero then put_listent must be a search function
102	 * and can just scan through what we have.
103	 */
104	if (context->bufsize == 0 ||
105	    (XFS_ISRESET_CURSOR(cursor) &&
106             (dp->i_afp->if_bytes + sf->hdr.count * 16) < context->bufsize)) {
107		for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
108			error = context->put_listent(context,
109					   sfe->flags,
110					   sfe->nameval,
111					   (int)sfe->namelen,
112					   (int)sfe->valuelen,
113					   &sfe->nameval[sfe->namelen]);
114
115			/*
116			 * Either search callback finished early or
117			 * didn't fit it all in the buffer after all.
118			 */
119			if (context->seen_enough)
120				break;
121
122			if (error)
123				return error;
124			sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
125		}
126		trace_xfs_attr_list_sf_all(context);
127		return(0);
128	}
129
130	/* do no more for a search callback */
131	if (context->bufsize == 0)
132		return 0;
133
134	/*
135	 * It didn't all fit, so we have to sort everything on hashval.
136	 */
137	sbsize = sf->hdr.count * sizeof(*sbuf);
138	sbp = sbuf = kmem_alloc(sbsize, KM_SLEEP | KM_NOFS);
139
140	/*
141	 * Scan the attribute list for the rest of the entries, storing
142	 * the relevant info from only those that match into a buffer.
143	 */
144	nsbuf = 0;
145	for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
146		if (unlikely(
147		    ((char *)sfe < (char *)sf) ||
148		    ((char *)sfe >= ((char *)sf + dp->i_afp->if_bytes)))) {
149			XFS_CORRUPTION_ERROR("xfs_attr_shortform_list",
150					     XFS_ERRLEVEL_LOW,
151					     context->dp->i_mount, sfe);
152			kmem_free(sbuf);
153			return XFS_ERROR(EFSCORRUPTED);
154		}
155
156		sbp->entno = i;
157		sbp->hash = xfs_da_hashname(sfe->nameval, sfe->namelen);
158		sbp->name = sfe->nameval;
159		sbp->namelen = sfe->namelen;
160		/* These are bytes, and both on-disk, don't endian-flip */
161		sbp->valuelen = sfe->valuelen;
162		sbp->flags = sfe->flags;
163		sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
164		sbp++;
165		nsbuf++;
166	}
167
168	/*
169	 * Sort the entries on hash then entno.
170	 */
171	xfs_sort(sbuf, nsbuf, sizeof(*sbuf), xfs_attr_shortform_compare);
172
173	/*
174	 * Re-find our place IN THE SORTED LIST.
175	 */
176	count = 0;
177	cursor->initted = 1;
178	cursor->blkno = 0;
179	for (sbp = sbuf, i = 0; i < nsbuf; i++, sbp++) {
180		if (sbp->hash == cursor->hashval) {
181			if (cursor->offset == count) {
182				break;
183			}
184			count++;
185		} else if (sbp->hash > cursor->hashval) {
186			break;
187		}
188	}
189	if (i == nsbuf) {
190		kmem_free(sbuf);
191		return(0);
192	}
193
194	/*
195	 * Loop putting entries into the user buffer.
196	 */
197	for ( ; i < nsbuf; i++, sbp++) {
198		if (cursor->hashval != sbp->hash) {
199			cursor->hashval = sbp->hash;
200			cursor->offset = 0;
201		}
202		error = context->put_listent(context,
203					sbp->flags,
204					sbp->name,
205					sbp->namelen,
206					sbp->valuelen,
207					&sbp->name[sbp->namelen]);
208		if (error)
209			return error;
210		if (context->seen_enough)
211			break;
212		cursor->offset++;
213	}
214
215	kmem_free(sbuf);
216	return(0);
217}
218
219STATIC int
220xfs_attr_node_list(xfs_attr_list_context_t *context)
221{
222	attrlist_cursor_kern_t *cursor;
223	xfs_attr_leafblock_t *leaf;
224	xfs_da_intnode_t *node;
225	struct xfs_attr3_icleaf_hdr leafhdr;
226	struct xfs_da3_icnode_hdr nodehdr;
227	struct xfs_da_node_entry *btree;
228	int error, i;
229	struct xfs_buf *bp;
230	struct xfs_inode	*dp = context->dp;
231
232	trace_xfs_attr_node_list(context);
233
234	cursor = context->cursor;
235	cursor->initted = 1;
236
237	/*
238	 * Do all sorts of validation on the passed-in cursor structure.
239	 * If anything is amiss, ignore the cursor and look up the hashval
240	 * starting from the btree root.
241	 */
242	bp = NULL;
243	if (cursor->blkno > 0) {
244		error = xfs_da3_node_read(NULL, dp, cursor->blkno, -1,
245					      &bp, XFS_ATTR_FORK);
246		if ((error != 0) && (error != EFSCORRUPTED))
247			return(error);
248		if (bp) {
249			struct xfs_attr_leaf_entry *entries;
250
251			node = bp->b_addr;
252			switch (be16_to_cpu(node->hdr.info.magic)) {
253			case XFS_DA_NODE_MAGIC:
254			case XFS_DA3_NODE_MAGIC:
255				trace_xfs_attr_list_wrong_blk(context);
256				xfs_trans_brelse(NULL, bp);
257				bp = NULL;
258				break;
259			case XFS_ATTR_LEAF_MAGIC:
260			case XFS_ATTR3_LEAF_MAGIC:
261				leaf = bp->b_addr;
262				xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
263				entries = xfs_attr3_leaf_entryp(leaf);
264				if (cursor->hashval > be32_to_cpu(
265						entries[leafhdr.count - 1].hashval)) {
266					trace_xfs_attr_list_wrong_blk(context);
267					xfs_trans_brelse(NULL, bp);
268					bp = NULL;
269				} else if (cursor->hashval <= be32_to_cpu(
270						entries[0].hashval)) {
271					trace_xfs_attr_list_wrong_blk(context);
272					xfs_trans_brelse(NULL, bp);
273					bp = NULL;
274				}
275				break;
276			default:
277				trace_xfs_attr_list_wrong_blk(context);
278				xfs_trans_brelse(NULL, bp);
279				bp = NULL;
280			}
281		}
282	}
283
284	/*
285	 * We did not find what we expected given the cursor's contents,
286	 * so we start from the top and work down based on the hash value.
287	 * Note that start of node block is same as start of leaf block.
288	 */
289	if (bp == NULL) {
290		cursor->blkno = 0;
291		for (;;) {
292			__uint16_t magic;
293
294			error = xfs_da3_node_read(NULL, dp,
295						      cursor->blkno, -1, &bp,
296						      XFS_ATTR_FORK);
297			if (error)
298				return(error);
299			node = bp->b_addr;
300			magic = be16_to_cpu(node->hdr.info.magic);
301			if (magic == XFS_ATTR_LEAF_MAGIC ||
302			    magic == XFS_ATTR3_LEAF_MAGIC)
303				break;
304			if (magic != XFS_DA_NODE_MAGIC &&
305			    magic != XFS_DA3_NODE_MAGIC) {
306				XFS_CORRUPTION_ERROR("xfs_attr_node_list(3)",
307						     XFS_ERRLEVEL_LOW,
308						     context->dp->i_mount,
309						     node);
310				xfs_trans_brelse(NULL, bp);
311				return XFS_ERROR(EFSCORRUPTED);
312			}
313
314			dp->d_ops->node_hdr_from_disk(&nodehdr, node);
315			btree = dp->d_ops->node_tree_p(node);
316			for (i = 0; i < nodehdr.count; btree++, i++) {
317				if (cursor->hashval
318						<= be32_to_cpu(btree->hashval)) {
319					cursor->blkno = be32_to_cpu(btree->before);
320					trace_xfs_attr_list_node_descend(context,
321									 btree);
322					break;
323				}
324			}
325			if (i == nodehdr.count) {
326				xfs_trans_brelse(NULL, bp);
327				return 0;
328			}
329			xfs_trans_brelse(NULL, bp);
330		}
331	}
332	ASSERT(bp != NULL);
333
334	/*
335	 * Roll upward through the blocks, processing each leaf block in
336	 * order.  As long as there is space in the result buffer, keep
337	 * adding the information.
338	 */
339	for (;;) {
340		leaf = bp->b_addr;
341		error = xfs_attr3_leaf_list_int(bp, context);
342		if (error) {
343			xfs_trans_brelse(NULL, bp);
344			return error;
345		}
346		xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf);
347		if (context->seen_enough || leafhdr.forw == 0)
348			break;
349		cursor->blkno = leafhdr.forw;
350		xfs_trans_brelse(NULL, bp);
351		error = xfs_attr3_leaf_read(NULL, dp, cursor->blkno, -1, &bp);
352		if (error)
353			return error;
354	}
355	xfs_trans_brelse(NULL, bp);
356	return 0;
357}
358
359/*
360 * Copy out attribute list entries for attr_list(), for leaf attribute lists.
361 */
362int
363xfs_attr3_leaf_list_int(
364	struct xfs_buf			*bp,
365	struct xfs_attr_list_context	*context)
366{
367	struct attrlist_cursor_kern	*cursor;
368	struct xfs_attr_leafblock	*leaf;
369	struct xfs_attr3_icleaf_hdr	ichdr;
370	struct xfs_attr_leaf_entry	*entries;
371	struct xfs_attr_leaf_entry	*entry;
372	int				retval;
373	int				i;
374
375	trace_xfs_attr_list_leaf(context);
376
377	leaf = bp->b_addr;
378	xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf);
379	entries = xfs_attr3_leaf_entryp(leaf);
380
381	cursor = context->cursor;
382	cursor->initted = 1;
383
384	/*
385	 * Re-find our place in the leaf block if this is a new syscall.
386	 */
387	if (context->resynch) {
388		entry = &entries[0];
389		for (i = 0; i < ichdr.count; entry++, i++) {
390			if (be32_to_cpu(entry->hashval) == cursor->hashval) {
391				if (cursor->offset == context->dupcnt) {
392					context->dupcnt = 0;
393					break;
394				}
395				context->dupcnt++;
396			} else if (be32_to_cpu(entry->hashval) >
397					cursor->hashval) {
398				context->dupcnt = 0;
399				break;
400			}
401		}
402		if (i == ichdr.count) {
403			trace_xfs_attr_list_notfound(context);
404			return 0;
405		}
406	} else {
407		entry = &entries[0];
408		i = 0;
409	}
410	context->resynch = 0;
411
412	/*
413	 * We have found our place, start copying out the new attributes.
414	 */
415	retval = 0;
416	for (; i < ichdr.count; entry++, i++) {
417		if (be32_to_cpu(entry->hashval) != cursor->hashval) {
418			cursor->hashval = be32_to_cpu(entry->hashval);
419			cursor->offset = 0;
420		}
421
422		if (entry->flags & XFS_ATTR_INCOMPLETE)
423			continue;		/* skip incomplete entries */
424
425		if (entry->flags & XFS_ATTR_LOCAL) {
426			xfs_attr_leaf_name_local_t *name_loc =
427				xfs_attr3_leaf_name_local(leaf, i);
428
429			retval = context->put_listent(context,
430						entry->flags,
431						name_loc->nameval,
432						(int)name_loc->namelen,
433						be16_to_cpu(name_loc->valuelen),
434						&name_loc->nameval[name_loc->namelen]);
435			if (retval)
436				return retval;
437		} else {
438			xfs_attr_leaf_name_remote_t *name_rmt =
439				xfs_attr3_leaf_name_remote(leaf, i);
440
441			int valuelen = be32_to_cpu(name_rmt->valuelen);
442
443			if (context->put_value) {
444				xfs_da_args_t args;
445
446				memset((char *)&args, 0, sizeof(args));
447				args.dp = context->dp;
448				args.whichfork = XFS_ATTR_FORK;
449				args.valuelen = valuelen;
450				args.rmtvaluelen = valuelen;
451				args.value = kmem_alloc(valuelen, KM_SLEEP | KM_NOFS);
452				args.rmtblkno = be32_to_cpu(name_rmt->valueblk);
453				args.rmtblkcnt = xfs_attr3_rmt_blocks(
454							args.dp->i_mount, valuelen);
455				retval = xfs_attr_rmtval_get(&args);
456				if (retval)
457					return retval;
458				retval = context->put_listent(context,
459						entry->flags,
460						name_rmt->name,
461						(int)name_rmt->namelen,
462						valuelen,
463						args.value);
464				kmem_free(args.value);
465			} else {
466				retval = context->put_listent(context,
467						entry->flags,
468						name_rmt->name,
469						(int)name_rmt->namelen,
470						valuelen,
471						NULL);
472			}
473			if (retval)
474				return retval;
475		}
476		if (context->seen_enough)
477			break;
478		cursor->offset++;
479	}
480	trace_xfs_attr_list_leaf_end(context);
481	return retval;
482}
483
484/*
485 * Copy out attribute entries for attr_list(), for leaf attribute lists.
486 */
487STATIC int
488xfs_attr_leaf_list(xfs_attr_list_context_t *context)
489{
490	int error;
491	struct xfs_buf *bp;
492
493	trace_xfs_attr_leaf_list(context);
494
495	context->cursor->blkno = 0;
496	error = xfs_attr3_leaf_read(NULL, context->dp, 0, -1, &bp);
497	if (error)
498		return XFS_ERROR(error);
499
500	error = xfs_attr3_leaf_list_int(bp, context);
501	xfs_trans_brelse(NULL, bp);
502	return XFS_ERROR(error);
503}
504
505int
506xfs_attr_list_int(
507	xfs_attr_list_context_t *context)
508{
509	int error;
510	xfs_inode_t *dp = context->dp;
511	uint		lock_mode;
512
513	XFS_STATS_INC(xs_attr_list);
514
515	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
516		return EIO;
517
518	/*
519	 * Decide on what work routines to call based on the inode size.
520	 */
521	lock_mode = xfs_ilock_attr_map_shared(dp);
522	if (!xfs_inode_hasattr(dp)) {
523		error = 0;
524	} else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
525		error = xfs_attr_shortform_list(context);
526	} else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
527		error = xfs_attr_leaf_list(context);
528	} else {
529		error = xfs_attr_node_list(context);
530	}
531	xfs_iunlock(dp, lock_mode);
532	return error;
533}
534
535#define	ATTR_ENTBASESIZE		/* minimum bytes used by an attr */ \
536	(((struct attrlist_ent *) 0)->a_name - (char *) 0)
537#define	ATTR_ENTSIZE(namelen)		/* actual bytes used by an attr */ \
538	((ATTR_ENTBASESIZE + (namelen) + 1 + sizeof(u_int32_t)-1) \
539	 & ~(sizeof(u_int32_t)-1))
540
541/*
542 * Format an attribute and copy it out to the user's buffer.
543 * Take care to check values and protect against them changing later,
544 * we may be reading them directly out of a user buffer.
545 */
546STATIC int
547xfs_attr_put_listent(
548	xfs_attr_list_context_t *context,
549	int		flags,
550	unsigned char	*name,
551	int		namelen,
552	int		valuelen,
553	unsigned char	*value)
554{
555	struct attrlist *alist = (struct attrlist *)context->alist;
556	attrlist_ent_t *aep;
557	int arraytop;
558
559	ASSERT(!(context->flags & ATTR_KERNOVAL));
560	ASSERT(context->count >= 0);
561	ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
562	ASSERT(context->firstu >= sizeof(*alist));
563	ASSERT(context->firstu <= context->bufsize);
564
565	/*
566	 * Only list entries in the right namespace.
567	 */
568	if (((context->flags & ATTR_SECURE) == 0) !=
569	    ((flags & XFS_ATTR_SECURE) == 0))
570		return 0;
571	if (((context->flags & ATTR_ROOT) == 0) !=
572	    ((flags & XFS_ATTR_ROOT) == 0))
573		return 0;
574
575	arraytop = sizeof(*alist) +
576			context->count * sizeof(alist->al_offset[0]);
577	context->firstu -= ATTR_ENTSIZE(namelen);
578	if (context->firstu < arraytop) {
579		trace_xfs_attr_list_full(context);
580		alist->al_more = 1;
581		context->seen_enough = 1;
582		return 1;
583	}
584
585	aep = (attrlist_ent_t *)&context->alist[context->firstu];
586	aep->a_valuelen = valuelen;
587	memcpy(aep->a_name, name, namelen);
588	aep->a_name[namelen] = 0;
589	alist->al_offset[context->count++] = context->firstu;
590	alist->al_count = context->count;
591	trace_xfs_attr_list_add(context);
592	return 0;
593}
594
595/*
596 * Generate a list of extended attribute names and optionally
597 * also value lengths.  Positive return value follows the XFS
598 * convention of being an error, zero or negative return code
599 * is the length of the buffer returned (negated), indicating
600 * success.
601 */
602int
603xfs_attr_list(
604	xfs_inode_t	*dp,
605	char		*buffer,
606	int		bufsize,
607	int		flags,
608	attrlist_cursor_kern_t *cursor)
609{
610	xfs_attr_list_context_t context;
611	struct attrlist *alist;
612	int error;
613
614	/*
615	 * Validate the cursor.
616	 */
617	if (cursor->pad1 || cursor->pad2)
618		return(XFS_ERROR(EINVAL));
619	if ((cursor->initted == 0) &&
620	    (cursor->hashval || cursor->blkno || cursor->offset))
621		return XFS_ERROR(EINVAL);
622
623	/*
624	 * Check for a properly aligned buffer.
625	 */
626	if (((long)buffer) & (sizeof(int)-1))
627		return XFS_ERROR(EFAULT);
628	if (flags & ATTR_KERNOVAL)
629		bufsize = 0;
630
631	/*
632	 * Initialize the output buffer.
633	 */
634	memset(&context, 0, sizeof(context));
635	context.dp = dp;
636	context.cursor = cursor;
637	context.resynch = 1;
638	context.flags = flags;
639	context.alist = buffer;
640	context.bufsize = (bufsize & ~(sizeof(int)-1));  /* align */
641	context.firstu = context.bufsize;
642	context.put_listent = xfs_attr_put_listent;
643
644	alist = (struct attrlist *)context.alist;
645	alist->al_count = 0;
646	alist->al_more = 0;
647	alist->al_offset[0] = context.bufsize;
648
649	error = xfs_attr_list_int(&context);
650	ASSERT(error >= 0);
651	return error;
652}