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

📄 aops.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	if (unlikely(page->index >= (i_size + PAGE_CACHE_SIZE - 1) >>			PAGE_CACHE_SHIFT)) {		zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0);		ntfs_debug("Read outside i_size - truncated?");		goto done;	}	/*	 * This can potentially happen because we clear PageUptodate() during	 * ntfs_writepage() of MstProtected() attributes.	 */	if (PageUptodate(page)) {		unlock_page(page);		return 0;	}	ni = NTFS_I(vi);	/*	 * Only $DATA attributes can be encrypted and only unnamed $DATA	 * attributes can be compressed.  Index root can have the flags set but	 * this means to create compressed/encrypted files, not that the	 * attribute is compressed/encrypted.  Note we need to check for	 * AT_INDEX_ALLOCATION since this is the type of both directory and	 * index inodes.	 */	if (ni->type != AT_INDEX_ALLOCATION) {		/* If attribute is encrypted, deny access, just like NT4. */		if (NInoEncrypted(ni)) {			BUG_ON(ni->type != AT_DATA);			err = -EACCES;			goto err_out;		}		/* Compressed data streams are handled in compress.c. */		if (NInoNonResident(ni) && NInoCompressed(ni)) {			BUG_ON(ni->type != AT_DATA);			BUG_ON(ni->name_len);			return ntfs_read_compressed_block(page);		}	}	/* NInoNonResident() == NInoIndexAllocPresent() */	if (NInoNonResident(ni)) {		/* Normal, non-resident data stream. */		return ntfs_read_block(page);	}	/*	 * Attribute is resident, implying it is not compressed or encrypted.	 * This also means the attribute is smaller than an mft record and	 * hence smaller than a page, so can simply zero out any pages with	 * index above 0.  Note the attribute can actually be marked compressed	 * but if it is resident the actual data is not compressed so we are	 * ok to ignore the compressed flag here.	 */	if (unlikely(page->index > 0)) {		zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0);		goto done;	}	if (!NInoAttr(ni))		base_ni = ni;	else		base_ni = ni->ext.base_ntfs_ino;	/* Map, pin, and lock the mft record. */	mrec = map_mft_record(base_ni);	if (IS_ERR(mrec)) {		err = PTR_ERR(mrec);		goto err_out;	}	/*	 * If a parallel write made the attribute non-resident, drop the mft	 * record and retry the readpage.	 */	if (unlikely(NInoNonResident(ni))) {		unmap_mft_record(base_ni);		goto retry_readpage;	}	ctx = ntfs_attr_get_search_ctx(base_ni, mrec);	if (unlikely(!ctx)) {		err = -ENOMEM;		goto unm_err_out;	}	err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,			CASE_SENSITIVE, 0, NULL, 0, ctx);	if (unlikely(err))		goto put_unm_err_out;	attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);	read_lock_irqsave(&ni->size_lock, flags);	if (unlikely(attr_len > ni->initialized_size))		attr_len = ni->initialized_size;	i_size = i_size_read(vi);	read_unlock_irqrestore(&ni->size_lock, flags);	if (unlikely(attr_len > i_size)) {		/* Race with shrinking truncate. */		attr_len = i_size;	}	addr = kmap_atomic(page, KM_USER0);	/* Copy the data to the page. */	memcpy(addr, (u8*)ctx->attr +			le16_to_cpu(ctx->attr->data.resident.value_offset),			attr_len);	/* Zero the remainder of the page. */	memset(addr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);	flush_dcache_page(page);	kunmap_atomic(addr, KM_USER0);put_unm_err_out:	ntfs_attr_put_search_ctx(ctx);unm_err_out:	unmap_mft_record(base_ni);done:	SetPageUptodate(page);err_out:	unlock_page(page);	return err;}#ifdef NTFS_RW/** * ntfs_write_block - write a @page to the backing store * @page:	page cache page to write out * @wbc:	writeback control structure * * This function is for writing pages belonging to non-resident, non-mst * protected attributes to their backing store. * * For a page with buffers, map and write the dirty buffers asynchronously * under page writeback. For a page without buffers, create buffers for the * page, then proceed as above. * * If a page doesn't have buffers the page dirty state is definitive. If a page * does have buffers, the page dirty state is just a hint, and the buffer dirty * state is definitive. (A hint which has rules: dirty buffers against a clean * page is illegal. Other combinations are legal and need to be handled. In * particular a dirty page containing clean buffers for example.) * * Return 0 on success and -errno on error. * * Based on ntfs_read_block() and __block_write_full_page(). */static int ntfs_write_block(struct page *page, struct writeback_control *wbc){	VCN vcn;	LCN lcn;	s64 initialized_size;	loff_t i_size;	sector_t block, dblock, iblock;	struct inode *vi;	ntfs_inode *ni;	ntfs_volume *vol;	runlist_element *rl;	struct buffer_head *bh, *head;	unsigned long flags;	unsigned int blocksize, vcn_ofs;	int err;	bool need_end_writeback;	unsigned char blocksize_bits;	vi = page->mapping->host;	ni = NTFS_I(vi);	vol = ni->vol;	ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "			"0x%lx.", ni->mft_no, ni->type, page->index);	BUG_ON(!NInoNonResident(ni));	BUG_ON(NInoMstProtected(ni));	blocksize = vol->sb->s_blocksize;	blocksize_bits = vol->sb->s_blocksize_bits;	if (!page_has_buffers(page)) {		BUG_ON(!PageUptodate(page));		create_empty_buffers(page, blocksize,				(1 << BH_Uptodate) | (1 << BH_Dirty));		if (unlikely(!page_has_buffers(page))) {			ntfs_warning(vol->sb, "Error allocating page "					"buffers.  Redirtying page so we try "					"again later.");			/*			 * Put the page back on mapping->dirty_pages, but leave			 * its buffers' dirty state as-is.			 */			redirty_page_for_writepage(wbc, page);			unlock_page(page);			return 0;		}	}	bh = head = page_buffers(page);	BUG_ON(!bh);	/* NOTE: Different naming scheme to ntfs_read_block()! */	/* The first block in the page. */	block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);	read_lock_irqsave(&ni->size_lock, flags);	i_size = i_size_read(vi);	initialized_size = ni->initialized_size;	read_unlock_irqrestore(&ni->size_lock, flags);	/* The first out of bounds block for the data size. */	dblock = (i_size + blocksize - 1) >> blocksize_bits;	/* The last (fully or partially) initialized block. */	iblock = initialized_size >> blocksize_bits;	/*	 * Be very careful.  We have no exclusion from __set_page_dirty_buffers	 * here, and the (potentially unmapped) buffers may become dirty at	 * any time.  If a buffer becomes dirty here after we've inspected it	 * then we just miss that fact, and the page stays dirty.	 *	 * Buffers outside i_size may be dirtied by __set_page_dirty_buffers;	 * handle that here by just cleaning them.	 */	/*	 * Loop through all the buffers in the page, mapping all the dirty	 * buffers to disk addresses and handling any aliases from the	 * underlying block device's mapping.	 */	rl = NULL;	err = 0;	do {		bool is_retry = false;		if (unlikely(block >= dblock)) {			/*			 * Mapped buffers outside i_size will occur, because			 * this page can be outside i_size when there is a			 * truncate in progress. The contents of such buffers			 * were zeroed by ntfs_writepage().			 *			 * FIXME: What about the small race window where			 * ntfs_writepage() has not done any clearing because			 * the page was within i_size but before we get here,			 * vmtruncate() modifies i_size?			 */			clear_buffer_dirty(bh);			set_buffer_uptodate(bh);			continue;		}		/* Clean buffers are not written out, so no need to map them. */		if (!buffer_dirty(bh))			continue;		/* Make sure we have enough initialized size. */		if (unlikely((block >= iblock) &&				(initialized_size < i_size))) {			/*			 * If this page is fully outside initialized size, zero			 * out all pages between the current initialized size			 * and the current page. Just use ntfs_readpage() to do			 * the zeroing transparently.			 */			if (block > iblock) {				// TODO:				// For each page do:				// - read_cache_page()				// Again for each page do:				// - wait_on_page_locked()				// - Check (PageUptodate(page) &&				//			!PageError(page))				// Update initialized size in the attribute and				// in the inode.				// Again, for each page do:				//	__set_page_dirty_buffers();				// page_cache_release()				// We don't need to wait on the writes.				// Update iblock.			}			/*			 * The current page straddles initialized size. Zero			 * all non-uptodate buffers and set them uptodate (and			 * dirty?). Note, there aren't any non-uptodate buffers			 * if the page is uptodate.			 * FIXME: For an uptodate page, the buffers may need to			 * be written out because they were not initialized on			 * disk before.			 */			if (!PageUptodate(page)) {				// TODO:				// Zero any non-uptodate buffers up to i_size.				// Set them uptodate and dirty.			}			// TODO:			// Update initialized size in the attribute and in the			// inode (up to i_size).			// Update iblock.			// FIXME: This is inefficient. Try to batch the two			// size changes to happen in one go.			ntfs_error(vol->sb, "Writing beyond initialized size "					"is not supported yet. Sorry.");			err = -EOPNOTSUPP;			break;			// Do NOT set_buffer_new() BUT DO clear buffer range			// outside write request range.			// set_buffer_uptodate() on complete buffers as well as			// set_buffer_dirty().		}		/* No need to map buffers that are already mapped. */		if (buffer_mapped(bh))			continue;		/* Unmapped, dirty buffer. Need to map it. */		bh->b_bdev = vol->sb->s_bdev;		/* Convert block into corresponding vcn and offset. */		vcn = (VCN)block << blocksize_bits;		vcn_ofs = vcn & vol->cluster_size_mask;		vcn >>= vol->cluster_size_bits;		if (!rl) {lock_retry_remap:			down_read(&ni->runlist.lock);			rl = ni->runlist.rl;		}		if (likely(rl != NULL)) {			/* Seek to element containing target vcn. */			while (rl->length && rl[1].vcn <= vcn)				rl++;			lcn = ntfs_rl_vcn_to_lcn(rl, vcn);		} else			lcn = LCN_RL_NOT_MAPPED;		/* Successful remap. */		if (lcn >= 0) {			/* Setup buffer head to point to correct block. */			bh->b_blocknr = ((lcn << vol->cluster_size_bits) +					vcn_ofs) >> blocksize_bits;			set_buffer_mapped(bh);			continue;		}		/* It is a hole, need to instantiate it. */		if (lcn == LCN_HOLE) {			u8 *kaddr;			unsigned long *bpos, *bend;			/* Check if the buffer is zero. */			kaddr = kmap_atomic(page, KM_USER0);			bpos = (unsigned long *)(kaddr + bh_offset(bh));			bend = (unsigned long *)((u8*)bpos + blocksize);			do {				if (unlikely(*bpos))					break;			} while (likely(++bpos < bend));			kunmap_atomic(kaddr, KM_USER0);			if (bpos == bend) {				/*				 * Buffer is zero and sparse, no need to write				 * it.				 */				bh->b_blocknr = -1;				clear_buffer_dirty(bh);				continue;			}			// TODO: Instantiate the hole.			// clear_buffer_new(bh);			// unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);			ntfs_error(vol->sb, "Writing into sparse regions is "					"not supported yet. Sorry.");			err = -EOPNOTSUPP;			break;		}		/* If first try and runlist unmapped, map and retry. */		if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {			is_retry = true;			/*			 * Attempt to map runlist, dropping lock for			 * the duration.			 */			up_read(&ni->runlist.lock);			err = ntfs_map_runlist(ni, vcn);			if (likely(!err))				goto lock_retry_remap;			rl = NULL;		} else if (!rl)			up_read(&ni->runlist.lock);		/*		 * If buffer is outside the runlist, truncate has cut it out		 * of the runlist.  Just clean and clear the buffer and set it		 * uptodate so it can get discarded by the VM.		 */		if (err == -ENOENT || lcn == LCN_ENOENT) {			bh->b_blocknr = -1;			clear_buffer_dirty(bh);			zero_user_page(page, bh_offset(bh), blocksize,					KM_USER0);			set_buffer_uptodate(bh);			err = 0;			continue;		}		/* Failed to map the buffer, even after retrying. */		if (!err)			err = -EIO;		bh->b_blocknr = -1;		ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "				"attribute type 0x%x, vcn 0x%llx, offset 0x%x "				"because its location on disk could not be "				"determined%s (error code %i).", ni->mft_no,				ni->type, (unsigned long long)vcn,				vcn_ofs, is_retry ? " even after "				"retrying" : "", err);		break;	} while (block++, (bh = bh->b_this_page) != head);	/* Release the lock if we took it. */	if (rl)		up_read(&ni->runlist.lock);	/* For the error case, need to reset bh to the beginning. */	bh = head;	/* Just an optimization, so ->readpage() is not called later. */	if (unlikely(!PageUptodate(page))) {		int uptodate = 1;

⌨️ 快捷键说明

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