diff options
Diffstat (limited to 'fs/cifs/file.c')
| -rw-r--r-- | fs/cifs/file.c | 289 | 
1 files changed, 185 insertions, 104 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d843631c028..d7d65a70678 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -287,6 +287,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)  	struct inode *inode = cifs_file->dentry->d_inode;  	struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink);  	struct cifsInodeInfo *cifsi = CIFS_I(inode); +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct cifsLockInfo *li, *tmp;  	spin_lock(&cifs_file_list_lock); @@ -302,6 +303,13 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)  	if (list_empty(&cifsi->openFileList)) {  		cFYI(1, "closing last open instance for inode %p",  			cifs_file->dentry->d_inode); + +		/* in strict cache mode we need invalidate mapping on the last +		   close  because it may cause a error when we open this file +		   again and get at least level II oplock */ +		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) +			CIFS_I(inode)->invalid_mapping = true; +  		cifs_set_oplock_level(cifsi, 0);  	}  	spin_unlock(&cifs_file_list_lock); @@ -726,12 +734,12 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)  		/* BB we could chain these into one lock request BB */  		rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, -				 0, 1, lockType, 0 /* wait flag */ ); +				 0, 1, lockType, 0 /* wait flag */, 0);  		if (rc == 0) {  			rc = CIFSSMBLock(xid, tcon, netfid, length,  					 pfLock->fl_start, 1 /* numUnlock */ ,  					 0 /* numLock */ , lockType, -					 0 /* wait flag */ ); +					 0 /* wait flag */, 0);  			pfLock->fl_type = F_UNLCK;  			if (rc != 0)  				cERROR(1, "Error unlocking previously locked " @@ -748,13 +756,13 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)  				rc = CIFSSMBLock(xid, tcon, netfid, length,  					pfLock->fl_start, 0, 1,  					lockType | LOCKING_ANDX_SHARED_LOCK, -					0 /* wait flag */); +					0 /* wait flag */, 0);  				if (rc == 0) {  					rc = CIFSSMBLock(xid, tcon, netfid,  						length, pfLock->fl_start, 1, 0,  						lockType |  						LOCKING_ANDX_SHARED_LOCK, -						0 /* wait flag */); +						0 /* wait flag */, 0);  					pfLock->fl_type = F_RDLCK;  					if (rc != 0)  						cERROR(1, "Error unlocking " @@ -797,8 +805,8 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)  		if (numLock) {  			rc = CIFSSMBLock(xid, tcon, netfid, length, -					pfLock->fl_start, -					0, numLock, lockType, wait_flag); +					 pfLock->fl_start, 0, numLock, lockType, +					 wait_flag, 0);  			if (rc == 0) {  				/* For Windows locks we must store them. */ @@ -818,9 +826,9 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)  						(pfLock->fl_start + length) >=  						(li->offset + li->length)) {  					stored_rc = CIFSSMBLock(xid, tcon, -							netfid, -							li->length, li->offset, -							1, 0, li->type, false); +							netfid, li->length, +							li->offset, 1, 0, +							li->type, false, 0);  					if (stored_rc)  						rc = stored_rc;  					else { @@ -839,29 +847,6 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)  	return rc;  } -/* - * Set the timeout on write requests past EOF. For some servers (Windows) - * these calls can be very long. - * - * If we're writing >10M past the EOF we give a 180s timeout. Anything less - * than that gets a 45s timeout. Writes not past EOF get 15s timeouts. - * The 10M cutoff is totally arbitrary. A better scheme for this would be - * welcome if someone wants to suggest one. - * - * We may be able to do a better job with this if there were some way to - * declare that a file should be sparse. - */ -static int -cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset) -{ -	if (offset <= cifsi->server_eof) -		return CIFS_STD_OP; -	else if (offset > (cifsi->server_eof + (10 * 1024 * 1024))) -		return CIFS_VLONG_OP; -	else -		return CIFS_LONG_OP; -} -  /* update the file size (if needed) after a write */  static void  cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, @@ -882,7 +867,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,  	unsigned int total_written;  	struct cifs_sb_info *cifs_sb;  	struct cifsTconInfo *pTcon; -	int xid, long_op; +	int xid;  	struct cifsFileInfo *open_file;  	struct cifsInodeInfo *cifsi = CIFS_I(inode); @@ -903,7 +888,6 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,  	xid = GetXid(); -	long_op = cifs_write_timeout(cifsi, *poffset);  	for (total_written = 0; write_size > total_written;  	     total_written += bytes_written) {  		rc = -EAGAIN; @@ -931,7 +915,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,  				min_t(const int, cifs_sb->wsize,  				      write_size - total_written),  				*poffset, &bytes_written, -				NULL, write_data + total_written, long_op); +				NULL, write_data + total_written, 0);  		}  		if (rc || (bytes_written == 0)) {  			if (total_written) @@ -944,8 +928,6 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,  			cifs_update_eof(cifsi, *poffset, bytes_written);  			*poffset += bytes_written;  		} -		long_op = CIFS_STD_OP; /* subsequent writes fast - -				    15 seconds is plenty */  	}  	cifs_stats_bytes_written(pTcon, total_written); @@ -974,7 +956,7 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file,  	unsigned int total_written;  	struct cifs_sb_info *cifs_sb;  	struct cifsTconInfo *pTcon; -	int xid, long_op; +	int xid;  	struct dentry *dentry = open_file->dentry;  	struct cifsInodeInfo *cifsi = CIFS_I(dentry->d_inode); @@ -987,7 +969,6 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file,  	xid = GetXid(); -	long_op = cifs_write_timeout(cifsi, *poffset);  	for (total_written = 0; write_size > total_written;  	     total_written += bytes_written) {  		rc = -EAGAIN; @@ -1017,7 +998,7 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file,  				rc = CIFSSMBWrite2(xid, pTcon,  						open_file->netfid, len,  						*poffset, &bytes_written, -						iov, 1, long_op); +						iov, 1, 0);  			} else  				rc = CIFSSMBWrite(xid, pTcon,  					 open_file->netfid, @@ -1025,7 +1006,7 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file,  					       write_size - total_written),  					 *poffset, &bytes_written,  					 write_data + total_written, -					 NULL, long_op); +					 NULL, 0);  		}  		if (rc || (bytes_written == 0)) {  			if (total_written) @@ -1038,8 +1019,6 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file,  			cifs_update_eof(cifsi, *poffset, bytes_written);  			*poffset += bytes_written;  		} -		long_op = CIFS_STD_OP; /* subsequent writes fast - -				    15 seconds is plenty */  	}  	cifs_stats_bytes_written(pTcon, total_written); @@ -1239,7 +1218,7 @@ static int cifs_writepages(struct address_space *mapping,  	struct pagevec pvec;  	int rc = 0;  	int scanned = 0; -	int xid, long_op; +	int xid;  	cifs_sb = CIFS_SB(mapping->host->i_sb); @@ -1377,43 +1356,67 @@ retry:  				break;  		}  		if (n_iov) { +retry_write:  			open_file = find_writable_file(CIFS_I(mapping->host),  							false);  			if (!open_file) {  				cERROR(1, "No writable handles for inode");  				rc = -EBADF;  			} else { -				long_op = cifs_write_timeout(cifsi, offset);  				rc = CIFSSMBWrite2(xid, tcon, open_file->netfid,  						   bytes_to_write, offset,  						   &bytes_written, iov, n_iov, -						   long_op); +						   0);  				cifsFileInfo_put(open_file); -				cifs_update_eof(cifsi, offset, bytes_written);  			} -			if (rc || bytes_written < bytes_to_write) { -				cERROR(1, "Write2 ret %d, wrote %d", -					  rc, bytes_written); -				mapping_set_error(mapping, rc); -			} else { +			cFYI(1, "Write2 rc=%d, wrote=%u", rc, bytes_written); + +			/* +			 * For now, treat a short write as if nothing got +			 * written. A zero length write however indicates +			 * ENOSPC or EFBIG. We have no way to know which +			 * though, so call it ENOSPC for now. EFBIG would +			 * get translated to AS_EIO anyway. +			 * +			 * FIXME: make it take into account the data that did +			 *	  get written +			 */ +			if (rc == 0) { +				if (bytes_written == 0) +					rc = -ENOSPC; +				else if (bytes_written < bytes_to_write) +					rc = -EAGAIN; +			} + +			/* retry on data-integrity flush */ +			if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) +				goto retry_write; + +			/* fix the stats and EOF */ +			if (bytes_written > 0) {  				cifs_stats_bytes_written(tcon, bytes_written); +				cifs_update_eof(cifsi, offset, bytes_written);  			}  			for (i = 0; i < n_iov; i++) {  				page = pvec.pages[first + i]; -				/* Should we also set page error on -				success rc but too little data written? */ -				/* BB investigate retry logic on temporary -				server crash cases and how recovery works -				when page marked as error */ -				if (rc) +				/* on retryable write error, redirty page */ +				if (rc == -EAGAIN) +					redirty_page_for_writepage(wbc, page); +				else if (rc != 0)  					SetPageError(page);  				kunmap(page);  				unlock_page(page);  				end_page_writeback(page);  				page_cache_release(page);  			} + +			if (rc != -EAGAIN) +				mapping_set_error(mapping, rc); +			else +				rc = 0; +  			if ((wbc->nr_to_write -= n_iov) <= 0)  				done = 1;  			index = next; @@ -1525,27 +1528,47 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,  	return rc;  } -int cifs_fsync(struct file *file, int datasync) +int cifs_strict_fsync(struct file *file, int datasync)  {  	int xid;  	int rc = 0;  	struct cifsTconInfo *tcon;  	struct cifsFileInfo *smbfile = file->private_data;  	struct inode *inode = file->f_path.dentry->d_inode; +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	xid = GetXid();  	cFYI(1, "Sync file - name: %s datasync: 0x%x",  		file->f_path.dentry->d_name.name, datasync); -	rc = filemap_write_and_wait(inode->i_mapping); -	if (rc == 0) { -		struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	if (!CIFS_I(inode)->clientCanCacheRead) +		cifs_invalidate_mapping(inode); -		tcon = tlink_tcon(smbfile->tlink); -		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) -			rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); -	} +	tcon = tlink_tcon(smbfile->tlink); +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) +		rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); + +	FreeXid(xid); +	return rc; +} + +int cifs_fsync(struct file *file, int datasync) +{ +	int xid; +	int rc = 0; +	struct cifsTconInfo *tcon; +	struct cifsFileInfo *smbfile = file->private_data; +	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); + +	xid = GetXid(); + +	cFYI(1, "Sync file - name: %s datasync: 0x%x", +		file->f_path.dentry->d_name.name, datasync); + +	tcon = tlink_tcon(smbfile->tlink); +	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) +		rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);  	FreeXid(xid);  	return rc; @@ -1596,42 +1619,42 @@ int cifs_flush(struct file *file, fl_owner_t id)  	return rc;  } -ssize_t cifs_user_read(struct file *file, char __user *read_data, -	size_t read_size, loff_t *poffset) +static ssize_t +cifs_iovec_read(struct file *file, const struct iovec *iov, +		 unsigned long nr_segs, loff_t *poffset)  { -	int rc = -EACCES; -	unsigned int bytes_read = 0; -	unsigned int total_read = 0; -	unsigned int current_read_size; +	int rc; +	int xid; +	unsigned int total_read, bytes_read = 0; +	size_t len, cur_len; +	int iov_offset = 0;  	struct cifs_sb_info *cifs_sb;  	struct cifsTconInfo *pTcon; -	int xid;  	struct cifsFileInfo *open_file; -	char *smb_read_data; -	char __user *current_offset;  	struct smb_com_read_rsp *pSMBr; +	char *read_data; + +	if (!nr_segs) +		return 0; + +	len = iov_length(iov, nr_segs); +	if (!len) +		return 0;  	xid = GetXid();  	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); -	if (file->private_data == NULL) { -		rc = -EBADF; -		FreeXid(xid); -		return rc; -	}  	open_file = file->private_data;  	pTcon = tlink_tcon(open_file->tlink);  	if ((file->f_flags & O_ACCMODE) == O_WRONLY)  		cFYI(1, "attempting read on write only file instance"); -	for (total_read = 0, current_offset = read_data; -	     read_size > total_read; -	     total_read += bytes_read, current_offset += bytes_read) { -		current_read_size = min_t(const int, read_size - total_read, -					  cifs_sb->rsize); +	for (total_read = 0; total_read < len; total_read += bytes_read) { +		cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);  		rc = -EAGAIN; -		smb_read_data = NULL; +		read_data = NULL; +  		while (rc == -EAGAIN) {  			int buf_type = CIFS_NO_BUFFER;  			if (open_file->invalidHandle) { @@ -1639,27 +1662,25 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,  				if (rc != 0)  					break;  			} -			rc = CIFSSMBRead(xid, pTcon, -					 open_file->netfid, -					 current_read_size, *poffset, -					 &bytes_read, &smb_read_data, -					 &buf_type); -			pSMBr = (struct smb_com_read_rsp *)smb_read_data; -			if (smb_read_data) { -				if (copy_to_user(current_offset, -						smb_read_data + -						4 /* RFC1001 length field */ + -						le16_to_cpu(pSMBr->DataOffset), -						bytes_read)) +			rc = CIFSSMBRead(xid, pTcon, open_file->netfid, +					 cur_len, *poffset, &bytes_read, +					 &read_data, &buf_type); +			pSMBr = (struct smb_com_read_rsp *)read_data; +			if (read_data) { +				char *data_offset = read_data + 4 + +						le16_to_cpu(pSMBr->DataOffset); +				if (memcpy_toiovecend(iov, data_offset, +						      iov_offset, bytes_read))  					rc = -EFAULT; -  				if (buf_type == CIFS_SMALL_BUFFER) -					cifs_small_buf_release(smb_read_data); +					cifs_small_buf_release(read_data);  				else if (buf_type == CIFS_LARGE_BUFFER) -					cifs_buf_release(smb_read_data); -				smb_read_data = NULL; +					cifs_buf_release(read_data); +				read_data = NULL; +				iov_offset += bytes_read;  			}  		} +  		if (rc || (bytes_read == 0)) {  			if (total_read) {  				break; @@ -1672,13 +1693,57 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,  			*poffset += bytes_read;  		}  	} +  	FreeXid(xid);  	return total_read;  } +ssize_t cifs_user_read(struct file *file, char __user *read_data, +		       size_t read_size, loff_t *poffset) +{ +	struct iovec iov; +	iov.iov_base = read_data; +	iov.iov_len = read_size; + +	return cifs_iovec_read(file, &iov, 1, poffset); +} + +static ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, +			       unsigned long nr_segs, loff_t pos) +{ +	ssize_t read; + +	read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos); +	if (read > 0) +		iocb->ki_pos = pos; + +	return read; +} + +ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, +			  unsigned long nr_segs, loff_t pos) +{ +	struct inode *inode; + +	inode = iocb->ki_filp->f_path.dentry->d_inode; + +	if (CIFS_I(inode)->clientCanCacheRead) +		return generic_file_aio_read(iocb, iov, nr_segs, pos); + +	/* +	 * In strict cache mode we need to read from the server all the time +	 * if we don't have level II oplock because the server can delay mtime +	 * change - so we can't make a decision about inode invalidating. +	 * And we can also fail with pagereading if there are mandatory locks +	 * on pages affected by this read but not on the region from pos to +	 * pos+len-1. +	 */ + +	return cifs_user_readv(iocb, iov, nr_segs, pos); +}  static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, -	loff_t *poffset) +			 loff_t *poffset)  {  	int rc = -EACCES;  	unsigned int bytes_read = 0; @@ -1746,6 +1811,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,  	return total_read;  } +int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) +{ +	int rc, xid; +	struct inode *inode = file->f_path.dentry->d_inode; + +	xid = GetXid(); + +	if (!CIFS_I(inode)->clientCanCacheRead) +		cifs_invalidate_mapping(inode); + +	rc = generic_file_mmap(file, vma); +	FreeXid(xid); +	return rc; +} +  int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)  {  	int rc, xid; @@ -2192,7 +2272,8 @@ void cifs_oplock_break(struct work_struct *work)  	 */  	if (!cfile->oplock_break_cancelled) {  		rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0, -				 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false); +				 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false, +				 cinode->clientCanCacheRead ? 1 : 0);  		cFYI(1, "Oplock release rc = %d", rc);  	}  |