📄 file.c
字号:
on the wire (so we do not get SMBWrite returning EBADF if writepages is racing with close. Note that writepages does not specify a file handle, so it is possible for a file to be opened twice, and the application close the "wrong" file handle - in these cases we delay long enough to allow the SMBWrite to get on the wire before the SMB Close. We allow total wait here over 45 seconds, more than oplock break time, and more than enough to allow any write to complete on the server, or to time out on the client */ while ((atomic_read(&pSMBFile->wrtPending) != 0) && (timeout <= 50000)) { cERROR(1, ("writes pending, delay free of handle")); msleep(timeout); timeout *= 8; } kfree(pSMBFile->search_resume_name); kfree(file->private_data); file->private_data = NULL; } else rc = -EBADF; read_lock(&GlobalSMBSeslock); if (list_empty(&(CIFS_I(inode)->openFileList))) { cFYI(1, ("closing last open instance for inode %p", inode)); /* if the file is not open we do not know if we can cache info on this inode, much less write behind and read ahead */ CIFS_I(inode)->clientCanCacheRead = FALSE; CIFS_I(inode)->clientCanCacheAll = FALSE; } read_unlock(&GlobalSMBSeslock); if ((rc == 0) && CIFS_I(inode)->write_behind_rc) rc = CIFS_I(inode)->write_behind_rc; FreeXid(xid); return rc;}int cifs_closedir(struct inode *inode, struct file *file){ int rc = 0; int xid; struct cifsFileInfo *pCFileStruct = (struct cifsFileInfo *)file->private_data; char *ptmp; cFYI(1, ("Closedir inode = 0x%p", inode)); xid = GetXid(); if (pCFileStruct) { struct cifsTconInfo *pTcon; struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; cFYI(1, ("Freeing private data in close dir")); if ((pCFileStruct->srch_inf.endOfSearch == FALSE) && (pCFileStruct->invalidHandle == FALSE)) { pCFileStruct->invalidHandle = TRUE; rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid); cFYI(1, ("Closing uncompleted readdir with rc %d", rc)); /* not much we can do if it fails anyway, ignore rc */ rc = 0; } ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; if (ptmp) { cFYI(1, ("closedir free smb buf in srch struct")); pCFileStruct->srch_inf.ntwrk_buf_start = NULL; if (pCFileStruct->srch_inf.smallBuf) cifs_small_buf_release(ptmp); else cifs_buf_release(ptmp); } ptmp = pCFileStruct->search_resume_name; if (ptmp) { cFYI(1, ("closedir free resume name")); pCFileStruct->search_resume_name = NULL; kfree(ptmp); } kfree(file->private_data); file->private_data = NULL; } /* BB can we lock the filestruct while this is going on? */ FreeXid(xid); 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; mutex_lock(&fid->lock_mutex); list_add(&li->llist, &fid->llist); mutex_unlock(&fid->lock_mutex); return 0;}int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock){ int rc, xid; __u32 numLock = 0; __u32 numUnlock = 0; __u64 length; int wait_flag = FALSE; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; __u16 netfid; __u8 lockType = LOCKING_ANDX_LARGE_FILES; int posix_locking; length = 1 + pfLock->fl_end - pfLock->fl_start; rc = -EACCES; xid = GetXid(); cFYI(1, ("Lock parm: 0x%x flockflags: " "0x%x flocktype: 0x%x start: %lld end: %lld", cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start, pfLock->fl_end)); if (pfLock->fl_flags & FL_POSIX) cFYI(1, ("Posix")); if (pfLock->fl_flags & FL_FLOCK) cFYI(1, ("Flock")); if (pfLock->fl_flags & FL_SLEEP) { cFYI(1, ("Blocking lock")); wait_flag = TRUE; } if (pfLock->fl_flags & FL_ACCESS) cFYI(1, ("Process suspended by mandatory locking - " "not implemented yet")); if (pfLock->fl_flags & FL_LEASE) cFYI(1, ("Lease on file - not implemented yet")); if (pfLock->fl_flags & (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE))) cFYI(1, ("Unknown lock flags 0x%x", pfLock->fl_flags)); if (pfLock->fl_type == F_WRLCK) { cFYI(1, ("F_WRLCK ")); numLock = 1; } else if (pfLock->fl_type == F_UNLCK) { cFYI(1, ("F_UNLCK")); numUnlock = 1; /* Check if unlock includes more than one lock range */ } else if (pfLock->fl_type == F_RDLCK) { cFYI(1, ("F_RDLCK")); lockType |= LOCKING_ANDX_SHARED_LOCK; numLock = 1; } else if (pfLock->fl_type == F_EXLCK) { cFYI(1, ("F_EXLCK")); numLock = 1; } else if (pfLock->fl_type == F_SHLCK) { cFYI(1, ("F_SHLCK")); lockType |= LOCKING_ANDX_SHARED_LOCK; numLock = 1; } else cFYI(1, ("Unknown type of lock")); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; if (file->private_data == NULL) { FreeXid(xid); return -EBADF; } 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 (posix_locking) { int posix_lock_type; if (lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; rc = CIFSSMBPosixLock(xid, pTcon, netfid, 1 /* get */, length, pfLock, posix_lock_type, wait_flag); FreeXid(xid); return rc; } /* BB we could chain these into one lock request BB */ rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, 0, 1, lockType, 0 /* wait flag */ ); if (rc == 0) { rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, 1 /* numUnlock */ , 0 /* numLock */ , lockType, 0 /* wait flag */ ); pfLock->fl_type = F_UNLCK; if (rc != 0) cERROR(1, ("Error unlocking previously locked " "range %d during test of lock", rc)); rc = 0; } else { /* if rc == ERR_SHARING_VIOLATION ? */ rc = 0; /* do not change lock type to unlock since range in use */ } FreeXid(xid); return rc; } 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; else posix_lock_type = CIFS_WRLCK; if (numUnlock == 1) posix_lock_type = CIFS_UNLCK; rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */, length, pfLock, posix_lock_type, 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; rc = 0; mutex_lock(&fid->lock_mutex); list_for_each_entry_safe(li, tmp, &fid->llist, llist) { if (pfLock->fl_start <= li->offset && (pfLock->fl_start + length) >= (li->offset + 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); } } mutex_unlock(&fid->lock_mutex); } } if (pfLock->fl_flags & FL_POSIX) posix_lock_file_wait(file, pfLock); FreeXid(xid); return rc;}ssize_t cifs_user_write(struct file *file, const char __user *write_data, size_t write_size, loff_t *poffset){ int rc = 0; unsigned int bytes_written = 0; unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; /* cFYI(1, (" write %d bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name)); */ if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *) file->private_data; xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = CIFS_VLONG_OP; /* writes past EOF take long time */ else long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { if (file->private_data == NULL) { /* file has been closed on us */ FreeXid(xid); /* if we have gotten here we have written some data and blocked, and the file has been freed on us while we blocked so return what we managed to write */ return total_written; } if (open_file->closePend) { FreeXid(xid); if (total_written) return total_written; else return -EBADF; } if (open_file->invalidHandle) { /* we could deadlock if we called filemap_fdatawait from here so tell reopen_file not to flush data to server now */ rc = cifs_reopen_file(file, FALSE); if (rc != 0) break; } rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, write_size - total_written), *poffset, &bytes_written, NULL, write_data + total_written, long_op); } if (rc || (bytes_written == 0)) { if (total_written) break; else { FreeXid(xid); return rc; } } else *poffset += bytes_written; long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { struct inode *inode = file->f_path.dentry->d_inode;/* Do not update local mtime - server will set its actual value on write * inode->i_ctime = inode->i_mtime = * current_fs_time(inode->i_sb);*/ if (total_written > 0) { spin_lock(&inode->i_lock); if (*poffset > file->f_path.dentry->d_inode->i_size) i_size_write(file->f_path.dentry->d_inode, *poffset); spin_unlock(&inode->i_lock); } mark_inode_dirty_sync(file->f_path.dentry->d_inode); } FreeXid(xid); return total_written;}static ssize_t cifs_write(struct file *file, const char *write_data, size_t write_size, loff_t *poffset){ int rc = 0; unsigned int bytes_written = 0; unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; cFYI(1, ("write %zd bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name)); if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *)file->private_data; xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */ else long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { if (file->private_data == NULL) { /* file has been closed on us */ FreeXid(xid); /* if we have gotten here we have written some data and blocked, and the file has been freed on us while we blocked so return what we managed to write */ return total_written; } if (open_file->closePend) { FreeXid(xid); if (total_written) return total_written; else return -EBADF; } if (open_file->invalidHandle) { /* we could deadlock if we called filemap_fdatawait from here so tell reopen_file not to flush data to server now */ rc = cifs_reopen_file(file, FALSE); if (rc != 0) break; } if (experimEnabled || (pTcon->ses->server && ((pTcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0))) { struct kvec iov[2]; unsigned int len; len = min((size_t)cifs_sb->wsize, write_size - total_written); /* iov[0] is reserved for smb header */ iov[1].iov_base = (char *)write_data + total_written; iov[1].iov_len = len; rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid, len, *poffset, &bytes_written, iov, 1, long_op); } else rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, min_t(const int, cifs_sb->wsize, write_size - total_written), *poffset, &bytes_written, write_data + total_written, NULL, long_op); } if (rc || (bytes_written == 0)) { if (total_written) break; else { FreeXid(xid); return rc; } } else *poffset += bytes_written; long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); /* since the write may have blocked check these pointers again */ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) {/*BB We could make this contingent on superblock ATIME flag too *//* file->f_path.dentry->d_inode->i_ctime = file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/ if (total_written > 0) { spin_lock(&file->f_path.dentry->d_inode->i_lock); if (*poffset > file->f_path.dentry->d_inode->i_size) i_size_write(file->f_path.dentry->d_inode, *poffset); spin_unlock(&file->f_path.dentry->d_inode->i_lock); } mark_inode_dirty_sync(file->f_path.dentry->d_inode); } FreeXid(xid); return total_written;}#ifdef CONFIG_CIFS_EXPERIMENTALstruct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode){ struct cifsFileInfo *open_file = NULL; read_lock(&GlobalSMBSeslock); /* we could simply get the first_list_entry since write-only entries are always at the end of the list but since the first entry might have a close pending, we go through the whole list */ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend) continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_RDONLY))) { if (!open_file->invalidHandle) { /* found a good file */ /* lock it so it will not be closed on us */ atomic_inc(&open_file->wrtPending); read_unlock(&GlobalSMBSeslock); return open_file; } /* else might as well continue, and look for another, or simply have the caller reopen it again rather than trying to fix this handle */ } else /* write only file */ break; /* write only files are last so must be done */ } read_unlock(&GlobalSMBSeslock); return NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -