Esempio n. 1
0
/**
 * ntfs_inode_add_attrlist - add attribute list to inode and fill it
 * @ni: opened ntfs inode to which add attribute list
 *
 * Return 0 on success or -1 on error with errno set to the error code.
 * The following error codes are defined:
 *	EINVAL	- Invalid arguments were passed to the function.
 *	EEXIST	- Attribute list already exist.
 *	EIO	- Input/Ouput error occurred.
 *	ENOMEM	- Not enough memory to perform add.
 */
int ntfs_inode_add_attrlist(ntfs_inode *ni)
{
	int err;
	ntfs_attr_search_ctx *ctx;
	u8 *al = NULL, *aln;
	int al_len = 0;
	ATTR_LIST_ENTRY *ale = NULL;
	ntfs_attr *na;

	if (!ni) {
		errno = EINVAL;
		ntfs_log_perror("%s", __FUNCTION__);
		return -1;
	}

	ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no);

	if (NInoAttrList(ni) || ni->nr_extents) {
		errno = EEXIST;
		ntfs_log_perror("Inode already has attribute list");
		return -1;
	}

	/* Form attribute list. */
	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!ctx) {
		err = errno;
		goto err_out;
	}
	/* Walk through all attributes. */
	while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) {

		int ale_size;

		if (ctx->attr->type == AT_ATTRIBUTE_LIST) {
			err = EIO;
			ntfs_log_perror("Attribute list already present");
			goto put_err_out;
		}

		ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
					ctx->attr->name_length + 7) & ~7;
		al_len += ale_size;

		aln = realloc(al, al_len);
		if (!aln) {
			err = errno;
			ntfs_log_perror("Failed to realloc %d bytes", al_len);
			goto put_err_out;
		}
		ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al));
		al = aln;

		memset(ale, 0, ale_size);

		/* Add attribute to attribute list. */
		ale->type = ctx->attr->type;
		ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) +
			sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7);
		ale->name_length = ctx->attr->name_length;
		ale->name_offset = (u8 *)ale->name - (u8 *)ale;
		if (ctx->attr->non_resident)
			ale->lowest_vcn = ctx->attr->lowest_vcn;
		else
			ale->lowest_vcn = 0;
		ale->mft_reference = MK_LE_MREF(ni->mft_no,
			le16_to_cpu(ni->mrec->sequence_number));
		ale->instance = ctx->attr->instance;
		memcpy(ale->name, (u8 *)ctx->attr +
				le16_to_cpu(ctx->attr->name_offset),
				ctx->attr->name_length * sizeof(ntfschar));
		ale = (ATTR_LIST_ENTRY *)(al + al_len);
	}
	/* Check for real error occurred. */
	if (errno != ENOENT) {
		err = errno;
		ntfs_log_perror("%s: Attribute lookup failed, inode %lld",
				__FUNCTION__, (long long)ni->mft_no);
		goto put_err_out;
	}

	/* Set in-memory attribute list. */
	ni->attr_list = al;
	ni->attr_list_size = al_len;
	NInoSetAttrList(ni);
	NInoAttrListSetDirty(ni);

	/* Free space if there is not enough it for $ATTRIBUTE_LIST. */
	if (le32_to_cpu(ni->mrec->bytes_allocated) -
			le32_to_cpu(ni->mrec->bytes_in_use) <
			offsetof(ATTR_RECORD, resident_end)) {
		if (ntfs_inode_free_space(ni,
				offsetof(ATTR_RECORD, resident_end))) {
			/* Failed to free space. */
			err = errno;
			ntfs_log_perror("Failed to free space for attrlist");
			goto rollback;
		}
	}

	/* Add $ATTRIBUTE_LIST to mft record. */
	if (ntfs_resident_attr_record_add(ni,
				AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) {
		err = errno;
		ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT");
		goto rollback;
	}

	/* Resize it. */
	na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
	if (!na) {
		err = errno;
		ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST");
		goto remove_attrlist_record;
	}
	if (ntfs_attr_truncate(na, al_len)) {
		err = errno;
		ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST");
		ntfs_attr_close(na);
		goto remove_attrlist_record;;
	}

	ntfs_attr_put_search_ctx(ctx);
	ntfs_attr_close(na);
	return 0;

remove_attrlist_record:
	/* Prevent ntfs_attr_recorm_rm from freeing attribute list. */
	ni->attr_list = NULL;
	NInoClearAttrList(ni);
	/* Remove $ATTRIBUTE_LIST record. */
	ntfs_attr_reinit_search_ctx(ctx);
	if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0,
				CASE_SENSITIVE, 0, NULL, 0, ctx)) {
		if (ntfs_attr_record_rm(ctx))
			ntfs_log_perror("Rollback failed to remove attrlist");
	} else
		ntfs_log_perror("Rollback failed to find attrlist");
	/* Setup back in-memory runlist. */
	ni->attr_list = al;
	ni->attr_list_size = al_len;
	NInoSetAttrList(ni);
rollback:
	/*
	 * Scan attribute list for attributes that placed not in the base MFT
	 * record and move them to it.
	 */
	ntfs_attr_reinit_search_ctx(ctx);
	ale = (ATTR_LIST_ENTRY*)al;
	while ((u8*)ale < al + al_len) {
		if (MREF_LE(ale->mft_reference) != ni->mft_no) {
			if (!ntfs_attr_lookup(ale->type, ale->name,
						ale->name_length,
						CASE_SENSITIVE,
						sle64_to_cpu(ale->lowest_vcn),
						NULL, 0, ctx)) {
				if (ntfs_attr_record_move_to(ctx, ni))
					ntfs_log_perror("Rollback failed to "
							"move attribute");
			} else
				ntfs_log_perror("Rollback failed to find attr");
			ntfs_attr_reinit_search_ctx(ctx);
		}
		ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length));
	}
	/* Remove in-memory attribute list. */
	ni->attr_list = NULL;
	ni->attr_list_size = 0;
	NInoClearAttrList(ni);
	NInoAttrListClearDirty(ni);
put_err_out:
	ntfs_attr_put_search_ctx(ctx);
err_out:
	free(al);
	errno = err;
	return -1;
}
Esempio n. 2
0
/**
 * ntfs_inode_sync_file_name - update FILE_NAME attributes
 * @ni:		ntfs inode to update FILE_NAME attributes
 *
 * Update all FILE_NAME attributes for inode @ni in the index.
 *
 * Return 0 on success or -1 on error with errno set to the error code.
 */
static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni)
{
	ntfs_attr_search_ctx *ctx = NULL;
	ntfs_index_context *ictx;
	ntfs_inode *index_ni;
	FILE_NAME_ATTR *fn;
	FILE_NAME_ATTR *fnx;
	REPARSE_POINT *rpp;
	le32 reparse_tag;
	int err = 0;

	ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no);

	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!ctx) {
		err = errno;
		goto err_out;
	}
	/* Collect the reparse tag, if any */
	reparse_tag = cpu_to_le32(0);
	if (ni->flags & FILE_ATTR_REPARSE_POINT) {
		if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL,
				0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
			rpp = (REPARSE_POINT*)((u8 *)ctx->attr +
					le16_to_cpu(ctx->attr->value_offset));
			reparse_tag = rpp->reparse_tag;
		}
		ntfs_attr_reinit_search_ctx(ctx);
	}
	/* Walk through all FILE_NAME attributes and update them. */
	while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) {
		fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr +
				le16_to_cpu(ctx->attr->value_offset));
		if (MREF_LE(fn->parent_directory) == ni->mft_no) {
			/*
			 * WARNING: We cheat here and obtain 2 attribute
			 * search contexts for one inode (first we obtained
			 * above, second will be obtained inside
			 * ntfs_index_lookup), it's acceptable for library,
			 * but will deadlock in the kernel.
			 */
			index_ni = ni;
		} else
			if (dir_ni)
				index_ni = dir_ni;
			else
				index_ni = ntfs_inode_open(ni->vol,
					le64_to_cpu(fn->parent_directory));
		if (!index_ni) {
			if (!err)
				err = errno;
			ntfs_log_perror("Failed to open inode %lld with index",
				(long long)le64_to_cpu(fn->parent_directory));
			continue;
		}
		ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4);
		if (!ictx) {
			if (!err)
				err = errno;
			ntfs_log_perror("Failed to get index ctx, inode %lld",
					(long long)index_ni->mft_no);
			if ((ni != index_ni) && !dir_ni
			    && ntfs_inode_close(index_ni) && !err)
				err = errno;
			continue;
		}
		if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) {
			if (!err) {
				if (errno == ENOENT)
					err = EIO;
				else
					err = errno;
			}
			ntfs_log_perror("Index lookup failed, inode %lld",
					(long long)index_ni->mft_no);
			ntfs_index_ctx_put(ictx);
			if (ni != index_ni && ntfs_inode_close(index_ni) && !err)
				err = errno;
			continue;
		}
		/* Update flags and file size. */
		fnx = (FILE_NAME_ATTR *)ictx->data;
		fnx->file_attributes =
				(fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) |
				(ni->flags & FILE_ATTR_VALID_FLAGS);
		if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
			fnx->data_size = fnx->allocated_size
				= const_cpu_to_le64(0);
		else {
			fnx->allocated_size = cpu_to_sle64(ni->allocated_size);
			fnx->data_size = cpu_to_sle64(ni->data_size);
			/*
			 * The file name record has also to be fixed if some
			 * attribute update implied the unnamed data to be
			 * made non-resident
			 */
			fn->allocated_size = fnx->allocated_size;
		}
			/* update or clear the reparse tag in the index */
		fnx->reparse_point_tag = reparse_tag;
		if (!test_nino_flag(ni, TimesSet)) {
			fnx->creation_time = ni->creation_time;
			fnx->last_data_change_time = ni->last_data_change_time;
			fnx->last_mft_change_time = ni->last_mft_change_time;
			fnx->last_access_time = ni->last_access_time;
		} else {
			fnx->creation_time = fn->creation_time;
			fnx->last_data_change_time = fn->last_data_change_time;
			fnx->last_mft_change_time = fn->last_mft_change_time;
			fnx->last_access_time = fn->last_access_time;
		}
		ntfs_index_entry_mark_dirty(ictx);
		ntfs_index_ctx_put(ictx);
		if ((ni != index_ni) && !dir_ni
		    && ntfs_inode_close(index_ni) && !err)
			err = errno;
	}
	/* Check for real error occurred. */
	if (errno != ENOENT) {
		err = errno;
		ntfs_log_perror("Attribute lookup failed, inode %lld",
				(long long)ni->mft_no);
		goto err_out;
	}
	ntfs_attr_put_search_ctx(ctx);
	if (err) {
		errno = err;
		return -1;
	}
	return 0;
err_out:
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);
	errno = err;
	return -1;
}
Esempio n. 3
0
/**
 * ntfs_inode_free_space - free space in the MFT record of an inode
 * @ni:		ntfs inode in which MFT record needs more free space
 * @size:	amount of space needed to free
 *
 * Return 0 on success or -1 on error with errno set to the error code.
 */
int ntfs_inode_free_space(ntfs_inode *ni, int size)
{
	ntfs_attr_search_ctx *ctx;
	int freed;

	if (!ni || size < 0) {
		errno = EINVAL;
		ntfs_log_perror("%s: ni=%p size=%d", __FUNCTION__, ni, size);
		return -1;
	}

	ntfs_log_trace("Entering for inode %lld, size %d\n",
		       (unsigned long long)ni->mft_no, size);

	freed = (le32_to_cpu(ni->mrec->bytes_allocated) -
				le32_to_cpu(ni->mrec->bytes_in_use));

	if (size <= freed)
		return 0;

	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!ctx)
		return -1;
	/*
	 * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT
	 * record, so position search context on the first attribute after them.
	 */
	if (ntfs_attr_position(AT_FILE_NAME, ctx))
		goto put_err_out;

	while (1) {
		int record_size;
		/*
		 * Check whether attribute is from different MFT record. If so,
		 * find next, because we don't need such.
		 */
		while (ctx->ntfs_ino->mft_no != ni->mft_no) {
retry:
			if (ntfs_attr_position(AT_UNUSED, ctx))
				goto put_err_out;
		}

		if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT &&
		    ctx->attr->type == AT_DATA)
			goto retry;

		if (ctx->attr->type == AT_INDEX_ROOT)
			goto retry;

		record_size = le32_to_cpu(ctx->attr->length);

		if (ntfs_attr_record_move_away(ctx, 0)) {
			ntfs_log_perror("Failed to move out attribute #2");
			break;
		}
		freed += record_size;

		/* Check whether we are done. */
		if (size <= freed) {
			ntfs_attr_put_search_ctx(ctx);
			return 0;
		}
		/*
		 * Reposition to first attribute after $STANDARD_INFORMATION
		 * and $ATTRIBUTE_LIST instead of simply skipping this attribute
		 * because in the case when we have got only in-memory attribute
		 * list then ntfs_attr_lookup will fail when it tries to find
		 * $ATTRIBUTE_LIST.
		 */
		ntfs_attr_reinit_search_ctx(ctx);
		if (ntfs_attr_position(AT_FILE_NAME, ctx))
			break;
	}
put_err_out:
	ntfs_attr_put_search_ctx(ctx);
	if (errno == ENOSPC)
		ntfs_log_trace("No attributes left that could be moved out.\n");
	return -1;
}
Esempio n. 4
0
int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size,
			int flags)
{
	ntfs_attr_search_ctx *ctx;
	STANDARD_INFORMATION *std_info;
	FILE_NAME_ATTR *fn;
	const u64 *times;
	ntfs_time now;
	int cnt;
	int ret;

	ret = -1;
	if ((size >= 8) && !(flags & XATTR_CREATE)) {
		times = (const u64*)value;
		now = ntfs_current_time();
			/* update the standard information attribute */
		ctx = ntfs_attr_get_search_ctx(ni, NULL);
		if (ctx) {
			if (ntfs_attr_lookup(AT_STANDARD_INFORMATION,
					AT_UNNAMED, 0, CASE_SENSITIVE,
					0, NULL, 0, ctx)) {
				ntfs_log_perror("Failed to get standard info (inode %lld)",
						(long long)ni->mft_no);
			} else {
				std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
					le16_to_cpu(ctx->attr->value_offset));
				/*
				 * Mark times set to avoid overwriting
				 * them when the inode is closed.
				 * The inode structure must also be updated
				 * (with loss of precision) because of cacheing.
				 * TODO : use NTFS precision in inode, and
				 * return sub-second times in getattr()
				 */
				set_nino_flag(ni, TimesSet);
				std_info->creation_time = cpu_to_le64(times[0]);
				ni->creation_time
					= std_info->creation_time;
				if (size >= 16) {
					std_info->last_data_change_time = cpu_to_le64(times[1]);
					ni->last_data_change_time
						= std_info->last_data_change_time;
				}
				if (size >= 24) {
					std_info->last_access_time = cpu_to_le64(times[2]);
					ni->last_access_time
						= std_info->last_access_time;
				}
				std_info->last_mft_change_time = now;
				ni->last_mft_change_time = now;
				ntfs_inode_mark_dirty(ctx->ntfs_ino);
				NInoFileNameSetDirty(ni);

				/* update the file names attributes */
				ntfs_attr_reinit_search_ctx(ctx);
				cnt = 0;
				while (!ntfs_attr_lookup(AT_FILE_NAME,
						AT_UNNAMED, 0, CASE_SENSITIVE,
						0, NULL, 0, ctx)) {
					fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr +
						le16_to_cpu(ctx->attr->value_offset));
					fn->creation_time
						= cpu_to_le64(times[0]);
					if (size >= 16)
						fn->last_data_change_time
							= cpu_to_le64(times[1]);
					if (size >= 24)
						fn->last_access_time
							= cpu_to_le64(times[2]);
					fn->last_mft_change_time = now;
					cnt++;
				}
				if (cnt)
					ret = 0;
				else {
					ntfs_log_perror("Failed to get file names (inode %lld)",
						(long long)ni->mft_no);
				}
			}
			ntfs_attr_put_search_ctx(ctx);
		}
	} else
		if (size < 8)
			errno = ERANGE;
		else
			errno = EEXIST;
	return (ret);
}