X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fcifs%2Ffile.c;fp=fs%2Fcifs%2Ffile.c;h=e9c5ba9084fc6a477ea13fdd26c0a666ddebc2f3;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=e2b4ce1dad6680cc54d8af516e7cf876533361fd;hpb=4e76c8a9fa413ccc09d3f7f664183dcce3555d57;p=linux-2.6.git diff --git a/fs/cifs/file.c b/fs/cifs/file.c index e2b4ce1da..e9c5ba908 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -5,6 +5,7 @@ * * Copyright (C) International Business Machines Corp., 2002,2003 * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -47,6 +48,8 @@ static inline struct cifsFileInfo *cifs_init_private( private_data->netfid = netfid; private_data->pid = current->tgid; init_MUTEX(&private_data->fh_sem); + init_MUTEX(&private_data->lock_sem); + INIT_LIST_HEAD(&private_data->llist); private_data->pfile = file; /* needed for writepage */ private_data->pInode = inode; private_data->invalidHandle = FALSE; @@ -110,7 +113,6 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, &pCifsInode->openFileList); } write_unlock(&GlobalSMBSeslock); - write_unlock(&file->f_owner.lock); if (pCifsInode->clientCanCacheRead) { /* we have the inode open somewhere else no need to discard cache data */ @@ -201,7 +203,7 @@ int cifs_open(struct inode *inode, struct file *file) } else { if (file->f_flags & O_EXCL) cERROR(1, ("could not find file instance for " - "new file %p ", file)); + "new file %p", file)); } } @@ -260,10 +262,15 @@ int cifs_open(struct inode *inode, struct file *file) rc = -ENOMEM; goto out; } - rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, - CREATE_NOT_DIR, &netfid, &oplock, buf, + + if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) + rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, + desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + else + rc = -EIO; /* no NT SMB support fall into legacy open below */ + if (rc == -EIO) { /* Old server, try legacy style OpenX */ rc = SMBLegacyOpen(xid, pTcon, full_path, disposition, @@ -272,7 +279,7 @@ int cifs_open(struct inode *inode, struct file *file) & CIFS_MOUNT_MAP_SPECIAL_CHR); } if (rc) { - cFYI(1, ("cifs_open returned 0x%x ", rc)); + cFYI(1, ("cifs_open returned 0x%x", rc)); goto out; } file->private_data = @@ -282,7 +289,6 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } pCifsFile = cifs_init_private(file->private_data, inode, file, netfid); - write_lock(&file->f_owner.lock); write_lock(&GlobalSMBSeslock); list_add(&pCifsFile->tlist, &pTcon->openFileList); @@ -293,7 +299,6 @@ int cifs_open(struct inode *inode, struct file *file) &oplock, buf, full_path, xid); } else { write_unlock(&GlobalSMBSeslock); - write_unlock(&file->f_owner.lock); } if (oplock & CIFS_CREATE_ACTION) { @@ -322,7 +327,7 @@ out: return rc; } -/* Try to reaquire byte range locks that were released when session */ +/* Try to reacquire byte range locks that were released when session */ /* to server was lost */ static int cifs_relock_file(struct cifsFileInfo *cifsFile) { @@ -409,8 +414,8 @@ static int cifs_reopen_file(struct inode *inode, struct file *file, CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { up(&pCifsFile->fh_sem); - cFYI(1, ("cifs_open returned 0x%x ", rc)); - cFYI(1, ("oplock: %d ", oplock)); + cFYI(1, ("cifs_open returned 0x%x", rc)); + cFYI(1, ("oplock: %d", oplock)); } else { pCifsFile->netfid = netfid; pCifsFile->invalidHandle = FALSE; @@ -471,8 +476,9 @@ int cifs_close(struct inode *inode, struct file *file) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; if (pSMBFile) { + struct cifsLockInfo *li, *tmp; + pSMBFile->closePend = TRUE; - write_lock(&file->f_owner.lock); if (pTcon) { /* no sense reconnecting to close a file that is already closed */ @@ -487,23 +493,28 @@ int cifs_close(struct inode *inode, struct file *file) the struct would be in each open file, but this should give enough time to clear the socket */ - write_unlock(&file->f_owner.lock); cERROR(1,("close with pending writes")); msleep(timeout); - write_lock(&file->f_owner.lock); timeout *= 4; } - write_unlock(&file->f_owner.lock); rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); - write_lock(&file->f_owner.lock); } } + + /* Delete any outstanding lock records. + We'll lose them when the file is closed anyway. */ + down(&pSMBFile->lock_sem); + list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) { + list_del(&li->llist); + kfree(li); + } + up(&pSMBFile->lock_sem); + write_lock(&GlobalSMBSeslock); list_del(&pSMBFile->flist); list_del(&pSMBFile->tlist); write_unlock(&GlobalSMBSeslock); - write_unlock(&file->f_owner.lock); kfree(pSMBFile->search_resume_name); kfree(file->private_data); file->private_data = NULL; @@ -531,7 +542,7 @@ int cifs_closedir(struct inode *inode, struct file *file) (struct cifsFileInfo *)file->private_data; char *ptmp; - cFYI(1, ("Closedir inode = 0x%p with ", inode)); + cFYI(1, ("Closedir inode = 0x%p", inode)); xid = GetXid(); @@ -574,6 +585,21 @@ int cifs_closedir(struct inode *inode, struct file *file) return rc; } +static int store_file_lock(struct cifsFileInfo *fid, __u64 len, + __u64 offset, __u8 lockType) +{ + struct cifsLockInfo *li = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); + if (li == NULL) + return -ENOMEM; + li->offset = offset; + li->length = len; + li->type = lockType; + down(&fid->lock_sem); + list_add(&li->llist, &fid->llist); + up(&fid->lock_sem); + return 0; +} + int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) { int rc, xid; @@ -585,6 +611,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) struct cifsTconInfo *pTcon; __u16 netfid; __u8 lockType = LOCKING_ANDX_LARGE_FILES; + int posix_locking; length = 1 + pfLock->fl_end - pfLock->fl_start; rc = -EACCES; @@ -605,7 +632,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) } if (pfLock->fl_flags & FL_ACCESS) cFYI(1, ("Process suspended by mandatory locking - " - "not implemented yet ")); + "not implemented yet")); if (pfLock->fl_flags & FL_LEASE) cFYI(1, ("Lease on file - not implemented yet")); if (pfLock->fl_flags & @@ -643,15 +670,14 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) } netfid = ((struct cifsFileInfo *)file->private_data)->netfid; + posix_locking = (cifs_sb->tcon->ses->capabilities & CAP_UNIX) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability)); /* BB add code here to normalize offset and length to account for negative length which we can not accept over the wire */ if (IS_GETLK(cmd)) { - if(experimEnabled && - (cifs_sb->tcon->ses->capabilities & CAP_UNIX) && - (CIFS_UNIX_FCNTL_CAP & - le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability))) { + if(posix_locking) { int posix_lock_type; if(lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; @@ -687,10 +713,15 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) FreeXid(xid); return rc; } - if (experimEnabled && - (cifs_sb->tcon->ses->capabilities & CAP_UNIX) && - (CIFS_UNIX_FCNTL_CAP & - le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability))) { + + if (!numLock && !numUnlock) { + /* if no lock or unlock then nothing + to do since we do not know what it is */ + FreeXid(xid); + return -EOPNOTSUPP; + } + + if (posix_locking) { int posix_lock_type; if(lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; @@ -699,18 +730,46 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) if(numUnlock == 1) posix_lock_type = CIFS_UNLCK; - else if(numLock == 0) { - /* if no lock or unlock then nothing - to do since we do not know what it is */ - FreeXid(xid); - return -EOPNOTSUPP; - } + rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */, length, pfLock, posix_lock_type, wait_flag); - } else - rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, - numUnlock, numLock, lockType, wait_flag); + } else { + struct cifsFileInfo *fid = (struct cifsFileInfo *)file->private_data; + + if (numLock) { + rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, + 0, numLock, lockType, wait_flag); + + if (rc == 0) { + /* For Windows locks we must store them. */ + rc = store_file_lock(fid, length, + pfLock->fl_start, lockType); + } + } else if (numUnlock) { + /* For each stored lock that this unlock overlaps + completely, unlock it. */ + int stored_rc = 0; + struct cifsLockInfo *li, *tmp; + + down(&fid->lock_sem); + list_for_each_entry_safe(li, tmp, &fid->llist, llist) { + if (pfLock->fl_start <= li->offset && + length >= li->length) { + stored_rc = CIFSSMBLock(xid, pTcon, netfid, + li->length, li->offset, + 1, 0, li->type, FALSE); + if (stored_rc) + rc = stored_rc; + + list_del(&li->llist); + kfree(li); + } + } + up(&fid->lock_sem); + } + } + if (pfLock->fl_flags & FL_POSIX) posix_lock_file_wait(file, pfLock); FreeXid(xid); @@ -1079,9 +1138,9 @@ static int cifs_writepages(struct address_space *mapping, unsigned int bytes_written; struct cifs_sb_info *cifs_sb; int done = 0; - pgoff_t end = -1; + pgoff_t end; pgoff_t index; - int is_range = 0; + int range_whole = 0; struct kvec iov[32]; int len; int n_iov = 0; @@ -1122,16 +1181,14 @@ static int cifs_writepages(struct address_space *mapping, xid = GetXid(); pagevec_init(&pvec, 0); - if (wbc->sync_mode == WB_SYNC_NONE) + if (wbc->range_cyclic) { index = mapping->writeback_index; /* Start from prev offset */ - else { - index = 0; - scanned = 1; - } - if (wbc->start || wbc->end) { - index = wbc->start >> PAGE_CACHE_SHIFT; - end = wbc->end >> PAGE_CACHE_SHIFT; - is_range = 1; + end = -1; + } else { + index = wbc->range_start >> PAGE_CACHE_SHIFT; + end = wbc->range_end >> PAGE_CACHE_SHIFT; + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) + range_whole = 1; scanned = 1; } retry: @@ -1167,7 +1224,7 @@ retry: break; } - if (unlikely(is_range) && (page->index > end)) { + if (!wbc->range_cyclic && page->index > end) { done = 1; unlock_page(page); break; @@ -1271,7 +1328,7 @@ retry: index = 0; goto retry; } - if (!is_range) + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = index; FreeXid(xid); @@ -1377,7 +1434,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) xid = GetXid(); - cFYI(1, ("Sync file - name: %s datasync: 0x%x ", + cFYI(1, ("Sync file - name: %s datasync: 0x%x", dentry->d_name.name, datasync)); rc = filemap_fdatawrite(inode->i_mapping); @@ -1406,7 +1463,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) /* fill in rpages then result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */ -/* cFYI(1, ("rpages is %d for sync page of Index %ld ", rpages, index)); +/* cFYI(1, ("rpages is %d for sync page of Index %ld", rpages, index)); #if 0 if (rc < 0) @@ -1419,7 +1476,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) * As file closes, flush all cached write data for this inode checking * for write behind errors. */ -int cifs_flush(struct file *file) +int cifs_flush(struct file *file, fl_owner_t id) { struct inode * inode = file->f_dentry->d_inode; int rc = 0; @@ -1838,7 +1895,7 @@ static int cifs_readpage_worker(struct file *file, struct page *page, if (rc < 0) goto io_error; else - cFYI(1, ("Bytes read %d ",rc)); + cFYI(1, ("Bytes read %d",rc)); file->f_dentry->d_inode->i_atime = current_fs_time(file->f_dentry->d_inode->i_sb); @@ -1948,7 +2005,7 @@ static int cifs_prepare_write(struct file *file, struct page *page, return 0; } -struct address_space_operations cifs_addr_ops = { +const struct address_space_operations cifs_addr_ops = { .readpage = cifs_readpage, .readpages = cifs_readpages, .writepage = cifs_writepage, @@ -1959,3 +2016,19 @@ struct address_space_operations cifs_addr_ops = { /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; + +/* + * cifs_readpages requires the server to support a buffer large enough to + * contain the header plus one complete page of data. Otherwise, we need + * to leave cifs_readpages out of the address space operations. + */ +const struct address_space_operations cifs_addr_ops_smallbuf = { + .readpage = cifs_readpage, + .writepage = cifs_writepage, + .writepages = cifs_writepages, + .prepare_write = cifs_prepare_write, + .commit_write = cifs_commit_write, + .set_page_dirty = __set_page_dirty_nobuffers, + /* .sync_page = cifs_sync_page, */ + /* .direct_IO = */ +};