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

📄 attrib.c

📁 一个在linux下挂载ntfs文件系统的好工具
💻 C
📖 第 1 页 / 共 5 页
字号:
	return NULL;}/** * ntfs_external_attr_find - find an attribute in the attribute list of an inode * @type:	attribute type to find * @name:	attribute name to find (optional, i.e. NULL means don't care) * @name_len:	attribute name length (only needed if @name present) * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) * @lowest_vcn:	lowest vcn to find (optional, non-resident attributes only) * @val:	attribute value to find (optional, resident attributes only) * @val_len:	attribute value length * @ctx:	search context with mft record and attribute to search from * * You shouldn't need to call this function directly. Use ntfs_attr_lookup() * instead. * * Find an attribute by searching the attribute list for the corresponding * attribute list entry. Having found the entry, map the mft record for read * if the attribute is in a different mft record/inode, find the attribute in * there and return it. * * If @type is AT_UNUSED, return the first found attribute, i.e. one can * enumerate all attributes by setting @type to AT_UNUSED and then calling * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to * ENOENT to indicate that there are no more entries. During the enumeration, * each successful call of ntfs_external_attr_find() will return the next * attribute described by the attribute list of the base mft record described * by the search context @ctx. * * If @type is AT_END, seek to the end of the base mft record ignoring the * attribute list completely and return -1 with errno set to ENOENT.  AT_END is * not a valid attribute, its length is zero for example, thus it is safer to * return error instead of success in this case. * * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, * match both named and unnamed attributes. * * On first search @ctx->ntfs_ino must be the inode of the base mft record and * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too * (@ctx->base_ntfs_ino is then the base inode). * * After finishing with the attribute/mft record you need to call * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any * mapped extent inodes, etc). * * Return 0 if the search was successful and -1 if not, with errno set to the * error code. * * On success, @ctx->attr is the found attribute, it is in mft record * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this * attribute with @ctx->base_* being the base mft record to which @ctx->attr * belongs. * * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the * attribute which collates just after the attribute being searched for in the * base ntfs inode, i.e. if one wants to add the attribute to the mft record * this is the correct place to insert it into, and if there is not enough * space, the attribute should be placed in an extent mft record. * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list * at which the new attribute's attribute list entry should be inserted.  The * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. * The only exception to this is when @type is AT_END, in which case * @ctx->al_entry is set to NULL also (see above). * * The following error codes are defined: *	ENOENT	Attribute not found, not an error as such. *	EINVAL	Invalid arguments. *	EIO	I/O error or corrupt data structures found. *	ENOMEM	Not enough memory to allocate necessary buffers. */static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,		const u32 name_len, const IGNORE_CASE_BOOL ic,		const VCN lowest_vcn, const u8 *val, const u32 val_len,		ntfs_attr_search_ctx *ctx){	ntfs_inode *base_ni, *ni;	ntfs_volume *vol;	ATTR_LIST_ENTRY *al_entry, *next_al_entry;	u8 *al_start, *al_end;	ATTR_RECORD *a;	ntfschar *al_name;	u32 al_name_len;	BOOL is_first_search = FALSE;	ni = ctx->ntfs_ino;	base_ni = ctx->base_ntfs_ino;	ntfs_log_trace("Entering for inode 0x%llx, attribute type 0x%x.\n",			(unsigned long long)ni->mft_no, type);	if (!base_ni) {		/* First call happens with the base mft record. */		base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino;		ctx->base_mrec = ctx->mrec;	}	if (ni == base_ni)		ctx->base_attr = ctx->attr;	if (type == AT_END)		goto not_found;	vol = base_ni->vol;	al_start = base_ni->attr_list;	al_end = al_start + base_ni->attr_list_size;	if (!ctx->al_entry) {		ctx->al_entry = (ATTR_LIST_ENTRY*)al_start;		is_first_search = TRUE;	}	/*	 * Iterate over entries in attribute list starting at @ctx->al_entry,	 * or the entry following that, if @ctx->is_first is TRUE.	 */	if (ctx->is_first) {		al_entry = ctx->al_entry;		ctx->is_first = FALSE;		/*		 * If an enumeration and the first attribute is higher than		 * the attribute list itself, need to return the attribute list		 * attribute.		 */		if ((type == AT_UNUSED) && is_first_search &&				le32_to_cpu(al_entry->type) >				le32_to_cpu(AT_ATTRIBUTE_LIST))			goto find_attr_list_attr;	} else {		al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry +				le16_to_cpu(ctx->al_entry->length));		/*		 * If this is an enumeration and the attribute list attribute		 * is the next one in the enumeration sequence, just return the		 * attribute list attribute from the base mft record as it is		 * not listed in the attribute list itself.		 */		if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) <				le32_to_cpu(AT_ATTRIBUTE_LIST) &&				le32_to_cpu(al_entry->type) >				le32_to_cpu(AT_ATTRIBUTE_LIST)) {			int rc;find_attr_list_attr:			/* Check for bogus calls. */			if (name || name_len || val || val_len || lowest_vcn) {				errno = EINVAL;				return -1;			}			/* We want the base record. */			ctx->ntfs_ino = base_ni;			ctx->mrec = ctx->base_mrec;			ctx->is_first = TRUE;			/* Sanity checks are performed elsewhere. */			ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +					le16_to_cpu(ctx->mrec->attrs_offset));			/* Find the attribute list attribute. */			rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0,					IGNORE_CASE, NULL, 0, ctx);			/*			 * Setup the search context so the correct			 * attribute is returned next time round.			 */			ctx->al_entry = al_entry;			ctx->is_first = TRUE;			/* Got it. Done. */			if (!rc)				return 0;			/* Error! If other than not found return it. */			if (errno != ENOENT)				return rc;			/* Not found?!? Absurd! Must be a bug... )-: */			ntfs_log_trace("BUG! Attribute list attribute not found but "					"it exists! Returning error (EINVAL).\n");			errno = EINVAL;			return -1;		}	}	for (;; al_entry = next_al_entry) {		/* Out of bounds check. */		if ((u8*)al_entry < base_ni->attr_list ||				(u8*)al_entry > al_end)			break;	/* Inode is corrupt. */		ctx->al_entry = al_entry;		/* Catch the end of the attribute list. */		if ((u8*)al_entry == al_end)			goto not_found;		if (!al_entry->length)			break;		if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +				le16_to_cpu(al_entry->length) > al_end)			break;		next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +				le16_to_cpu(al_entry->length));		if (type != AT_UNUSED) {			if (le32_to_cpu(al_entry->type) > le32_to_cpu(type))				goto not_found;			if (type != al_entry->type)				continue;		}		al_name_len = al_entry->name_length;		al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset);		/*		 * If !@type we want the attribute represented by this		 * attribute list entry.		 */		if (type == AT_UNUSED)			goto is_enumeration;		/*		 * If @name is AT_UNNAMED we want an unnamed attribute.		 * If @name is present, compare the two names.		 * Otherwise, match any attribute.		 */		if (name == AT_UNNAMED) {			if (al_name_len)				goto not_found;		} else if (name && !ntfs_names_are_equal(al_name, al_name_len,				name, name_len, ic, vol->upcase,				vol->upcase_len)) {			register int rc;			rc = ntfs_names_collate(name, name_len, al_name,					al_name_len, 1, IGNORE_CASE,					vol->upcase, vol->upcase_len);			/*			 * If @name collates before al_name, there is no			 * matching attribute.			 */			if (rc == -1)				goto not_found;			/* If the strings are not equal, continue search. */			if (rc)				continue;			/*			 * FIXME: Reverse engineering showed 0, IGNORE_CASE but			 * that is inconsistent with ntfs_attr_find(). The			 * subsequent rc checks were also different. Perhaps I			 * made a mistake in one of the two. Need to recheck			 * which is correct or at least see what is going			 * on... (AIA)			 */			rc = ntfs_names_collate(name, name_len, al_name,					al_name_len, 1, CASE_SENSITIVE,					vol->upcase, vol->upcase_len);			if (rc == -1)				goto not_found;			if (rc)				continue;		}		/*		 * The names match or @name not present and attribute is		 * unnamed. Now check @lowest_vcn. Continue search if the		 * next attribute list entry still fits @lowest_vcn. Otherwise		 * we have reached the right one or the search has failed.		 */		if (lowest_vcn && (u8*)next_al_entry >= al_start	    &&				(u8*)next_al_entry + 6 < al_end	    &&				(u8*)next_al_entry + le16_to_cpu(					next_al_entry->length) <= al_end    &&				sle64_to_cpu(next_al_entry->lowest_vcn) <=					lowest_vcn			    &&				next_al_entry->type == al_entry->type	    &&				next_al_entry->name_length == al_name_len   &&				ntfs_names_are_equal((ntfschar*)((char*)					next_al_entry +					next_al_entry->name_offset),					next_al_entry->name_length,					al_name, al_name_len, CASE_SENSITIVE,					vol->upcase, vol->upcase_len))			continue;is_enumeration:		if (MREF_LE(al_entry->mft_reference) == ni->mft_no) {			if (MSEQNO_LE(al_entry->mft_reference) !=					le16_to_cpu(					ni->mrec->sequence_number)) {				ntfs_log_debug("Found stale mft reference in "						"attribute list!\n");				break;			}		} else { /* Mft references do not match. */			/* Do we want the base record back? */			if (MREF_LE(al_entry->mft_reference) ==					base_ni->mft_no) {				ni = ctx->ntfs_ino = base_ni;				ctx->mrec = ctx->base_mrec;			} else {				/* We want an extent record. */				ni = ntfs_extent_inode_open(base_ni,						al_entry->mft_reference);				if (!ni) {					ntfs_log_perror("Failed to map extent inode");					break;				}				ctx->ntfs_ino = ni;				ctx->mrec = ni->mrec;			}			ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec +					le16_to_cpu(ctx->mrec->attrs_offset));		}		/*		 * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the		 * mft record containing the attribute represented by the		 * current al_entry.		 */		/*		 * We could call into ntfs_attr_find() to find the right		 * attribute in this mft record but this would be less		 * efficient and not quite accurate as ntfs_attr_find() ignores		 * the attribute instance numbers for example which become		 * important when one plays with attribute lists. Also, because		 * a proper match has been found in the attribute list entry		 * above, the comparison can now be optimized. So it is worth		 * re-implementing a simplified ntfs_attr_find() here.		 */		a = ctx->attr;		/*		 * Use a manual loop so we can still use break and continue		 * with the same meanings as above.		 */do_next_attr_loop:		if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec +				le32_to_cpu(ctx->mrec->bytes_allocated))			break;		if (a->type == AT_END)			continue;		if (!a->length)			break;		if (al_entry->instance != a->instance)			goto do_next_attr;		/*		 * If the type and/or the name are/is mismatched between the		 * attribute list entry and the attribute record, there is		 * corruption so we break and return error EIO.		 */		if (al_entry->type != a->type)			break;		if (!ntfs_names_are_equal((ntfschar*)((char*)a +				le16_to_cpu(a->name_offset)),				a->name_length, al_name,				al_name_len, CASE_SENSITIVE,				vol->upcase, vol->upcase_len))			break;		ctx->attr = a;		/*		 * If no @val specified or @val specified and it matches, we		 * have found it! Also, if !@type, it is an enumeration, so we		 * want the current attribute.		 */		if ((type == AT_UNUSED) || !val || (!a->non_resident &&				le32_to_cpu(a->value_length) == val_len &&				!memcmp((char*)a + le16_to_cpu(a->value_offset),				val, val_len))) {			return 0;		}do_next_attr:		/* Proceed to the next attribute in the current mft record. */		a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));		goto do_next_attr_loop;	}	if (ni != base_ni) {		ctx->ntfs_ino = base_ni;		ctx->mrec = ctx->base_mrec;		ctx->attr = ctx->base_attr;	}	ntfs_log_debug("Inode is corrupt.\n");	errno = EIO;	return -1;not_found:	/*	 * If we were looking for AT_END or we were enumerating and reached the	 * end, we reset the search context @ctx and use ntfs_attr_find() to	 * seek to the end of the base mft record.	 */	if (type == AT_UNUSED || type == AT_END) {		ntfs_attr_reinit_search_ctx(ctx);		return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len,				ctx);	}	/*	 * The attribute wasn't found.  Before we return, we want to ensure	 * @ctx->mrec and @ctx->attr indicate the position at which the	 * attribute should be inserted in the base mft record.  Since we also	 * want to preserve @ctx->al_entry we cannot reinitialize the search	 * context using ntfs_attr_reinit_search_ctx() as this would set	 * @ctx->al_entry to NULL.  Thus we do the necessary bits manually (see	 * ntfs_attr_init_search_ctx() below).  Note, we _only_ preserve	 * @ctx->al_entry as the remaining fields (base_*) are identical to	 * their non base_ counterparts and we cannot set @ctx->base_attr	 * correctly yet as we do not know what @ctx->attr will be set to by	 * the call to ntfs_attr_find() below.	 */	ctx->mrec = ctx->base_mrec;	ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +			le16_to_cpu(ctx->mrec->attrs_offset));	ctx->is_first = TRUE;	ctx->ntfs_ino = ctx->base_ntfs_ino;	ctx->base_ntfs_ino = NULL;	ctx->base_mrec = NULL;	ctx->base_attr = NULL;	/*	 * In case there are multiple matches in the base mft record, need to	 * keep enumerating until we get an attribute not found response (or	 * another error), otherwise we would keep returning the same attribute	 * over and over again and all programs using us for enumeration would	 * lock up in a tight loop.	 */	{		int ret;		do {			ret = ntfs_attr_find(type, name, name_len, ic, val,					val_len, ctx);		} while (!ret);		return ret;	}}/** * ntfs_attr_lookup - find an attribute in an ntfs inode * @type:	attribute type to find * @name:	attribute name to find (optional, i.e. NULL means don't care) * @name_len:	attribute name length (only needed if @name present) * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) * @lowest_vcn:	lowest vcn to find (optional, non-resident attributes only) * @val:	attribute value to find (optional, resident attributes only) * @val_len:	attribute value length * @ctx:	search context with mft record and attribute to search from *

⌨️ 快捷键说明

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