Loading fs/cifs/file.c +64 −57 Original line number Diff line number Diff line Loading @@ -459,8 +459,10 @@ int cifs_open(struct inode *inode, struct file *file) return rc; } /* Try to reacquire byte range locks that were released when session */ /* to server was lost */ /* * Try to reacquire byte range locks that were released when session * to server was lost */ static int cifs_relock_file(struct cifsFileInfo *cifsFile) { int rc = 0; Loading @@ -470,48 +472,53 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile) return rc; } static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) static int cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) { int rc = -EACCES; unsigned int xid; __u32 oplock; struct cifs_sb_info *cifs_sb; struct cifs_tcon *tcon; struct cifsInodeInfo *pCifsInode; struct TCP_Server_Info *server; struct cifsInodeInfo *cinode; struct inode *inode; char *full_path = NULL; int desiredAccess; int desired_access; int disposition = FILE_OPEN; int create_options = CREATE_NOT_DIR; __u16 netfid; struct cifs_fid fid; xid = get_xid(); mutex_lock(&pCifsFile->fh_mutex); if (!pCifsFile->invalidHandle) { mutex_unlock(&pCifsFile->fh_mutex); mutex_lock(&cfile->fh_mutex); if (!cfile->invalidHandle) { mutex_unlock(&cfile->fh_mutex); rc = 0; free_xid(xid); return rc; } inode = pCifsFile->dentry->d_inode; inode = cfile->dentry->d_inode; cifs_sb = CIFS_SB(inode->i_sb); tcon = tlink_tcon(pCifsFile->tlink); tcon = tlink_tcon(cfile->tlink); server = tcon->ses->server; /* can not grab rename sem here because various ops, including those that already have the rename sem can end up causing writepage to get called and if the server was down that means we end up here, and we can never tell if the caller already has the rename_sem */ full_path = build_path_from_dentry(pCifsFile->dentry); /* * Can not grab rename sem here because various ops, including those * that already have the rename sem can end up causing writepage to get * called and if the server was down that means we end up here, and we * can never tell if the caller already has the rename_sem. */ full_path = build_path_from_dentry(cfile->dentry); if (full_path == NULL) { rc = -ENOMEM; mutex_unlock(&pCifsFile->fh_mutex); mutex_unlock(&cfile->fh_mutex); free_xid(xid); return rc; } cFYI(1, "inode = 0x%p file flags 0x%x for %s", inode, pCifsFile->f_flags, full_path); cFYI(1, "inode = 0x%p file flags 0x%x for %s", inode, cfile->f_flags, full_path); if (tcon->ses->server->oplocks) oplock = REQ_OPLOCK; Loading @@ -525,69 +532,69 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) * O_CREAT, O_EXCL and O_TRUNC already had their effect on the * original open. Must mask them off for a reopen. */ unsigned int oflags = pCifsFile->f_flags & unsigned int oflags = cfile->f_flags & ~(O_CREAT | O_EXCL | O_TRUNC); rc = cifs_posix_open(full_path, NULL, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, oflags, &oplock, &netfid, xid); oflags, &oplock, &fid.netfid, xid); if (rc == 0) { cFYI(1, "posix reopen succeeded"); goto reopen_success; } /* fallthrough to retry open the old way on errors, especially in the reconnect path it is important to retry hard */ /* * fallthrough to retry open the old way on errors, especially * in the reconnect path it is important to retry hard */ } desiredAccess = cifs_convert_flags(pCifsFile->f_flags); desired_access = cifs_convert_flags(cfile->f_flags); if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; /* Can not refresh inode by passing in file_info buf to be returned by SMBOpen and then calling get_inode_info with returned buf since file might have write behind data that needs to be flushed and server version of file size can be stale. If we knew for sure that inode was not dirty locally we could do this */ rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess, create_options, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* * Can not refresh inode by passing in file_info buf to be returned by * CIFSSMBOpen and then calling get_inode_info with returned buf since * file might have write behind data that needs to be flushed and server * version of file size can be stale. If we knew for sure that inode was * not dirty locally we could do this. */ rc = server->ops->open(xid, tcon, full_path, disposition, desired_access, create_options, &fid, &oplock, NULL, cifs_sb); if (rc) { mutex_unlock(&pCifsFile->fh_mutex); cFYI(1, "cifs_open returned 0x%x", rc); mutex_unlock(&cfile->fh_mutex); cFYI(1, "cifs_reopen returned 0x%x", rc); cFYI(1, "oplock: %d", oplock); goto reopen_error_exit; } reopen_success: pCifsFile->fid.netfid = netfid; pCifsFile->invalidHandle = false; mutex_unlock(&pCifsFile->fh_mutex); pCifsInode = CIFS_I(inode); cfile->invalidHandle = false; mutex_unlock(&cfile->fh_mutex); cinode = CIFS_I(inode); if (can_flush) { rc = filemap_write_and_wait(inode->i_mapping); mapping_set_error(inode->i_mapping, rc); if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, xid); rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb, xid, NULL); } /* else we are writing out data to server already and could deadlock if we tried to flush data, and since we do not know if we have data that would invalidate the current end of file on the server we can not go to the server to get the new inod info */ cifs_set_oplock_level(pCifsInode, oplock); cifs_relock_file(pCifsFile); rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb, xid, NULL); } /* * Else we are writing out data to server already and could deadlock if * we tried to flush data, and since we do not know if we have data that * would invalidate the current end of file on the server we can not go * to the server to get the new inode info. */ server->ops->set_fid(cfile, &fid, oplock); cifs_relock_file(cfile); reopen_error_exit: kfree(full_path); Loading Loading
fs/cifs/file.c +64 −57 Original line number Diff line number Diff line Loading @@ -459,8 +459,10 @@ int cifs_open(struct inode *inode, struct file *file) return rc; } /* Try to reacquire byte range locks that were released when session */ /* to server was lost */ /* * Try to reacquire byte range locks that were released when session * to server was lost */ static int cifs_relock_file(struct cifsFileInfo *cifsFile) { int rc = 0; Loading @@ -470,48 +472,53 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile) return rc; } static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) static int cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) { int rc = -EACCES; unsigned int xid; __u32 oplock; struct cifs_sb_info *cifs_sb; struct cifs_tcon *tcon; struct cifsInodeInfo *pCifsInode; struct TCP_Server_Info *server; struct cifsInodeInfo *cinode; struct inode *inode; char *full_path = NULL; int desiredAccess; int desired_access; int disposition = FILE_OPEN; int create_options = CREATE_NOT_DIR; __u16 netfid; struct cifs_fid fid; xid = get_xid(); mutex_lock(&pCifsFile->fh_mutex); if (!pCifsFile->invalidHandle) { mutex_unlock(&pCifsFile->fh_mutex); mutex_lock(&cfile->fh_mutex); if (!cfile->invalidHandle) { mutex_unlock(&cfile->fh_mutex); rc = 0; free_xid(xid); return rc; } inode = pCifsFile->dentry->d_inode; inode = cfile->dentry->d_inode; cifs_sb = CIFS_SB(inode->i_sb); tcon = tlink_tcon(pCifsFile->tlink); tcon = tlink_tcon(cfile->tlink); server = tcon->ses->server; /* can not grab rename sem here because various ops, including those that already have the rename sem can end up causing writepage to get called and if the server was down that means we end up here, and we can never tell if the caller already has the rename_sem */ full_path = build_path_from_dentry(pCifsFile->dentry); /* * Can not grab rename sem here because various ops, including those * that already have the rename sem can end up causing writepage to get * called and if the server was down that means we end up here, and we * can never tell if the caller already has the rename_sem. */ full_path = build_path_from_dentry(cfile->dentry); if (full_path == NULL) { rc = -ENOMEM; mutex_unlock(&pCifsFile->fh_mutex); mutex_unlock(&cfile->fh_mutex); free_xid(xid); return rc; } cFYI(1, "inode = 0x%p file flags 0x%x for %s", inode, pCifsFile->f_flags, full_path); cFYI(1, "inode = 0x%p file flags 0x%x for %s", inode, cfile->f_flags, full_path); if (tcon->ses->server->oplocks) oplock = REQ_OPLOCK; Loading @@ -525,69 +532,69 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) * O_CREAT, O_EXCL and O_TRUNC already had their effect on the * original open. Must mask them off for a reopen. */ unsigned int oflags = pCifsFile->f_flags & unsigned int oflags = cfile->f_flags & ~(O_CREAT | O_EXCL | O_TRUNC); rc = cifs_posix_open(full_path, NULL, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, oflags, &oplock, &netfid, xid); oflags, &oplock, &fid.netfid, xid); if (rc == 0) { cFYI(1, "posix reopen succeeded"); goto reopen_success; } /* fallthrough to retry open the old way on errors, especially in the reconnect path it is important to retry hard */ /* * fallthrough to retry open the old way on errors, especially * in the reconnect path it is important to retry hard */ } desiredAccess = cifs_convert_flags(pCifsFile->f_flags); desired_access = cifs_convert_flags(cfile->f_flags); if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; /* Can not refresh inode by passing in file_info buf to be returned by SMBOpen and then calling get_inode_info with returned buf since file might have write behind data that needs to be flushed and server version of file size can be stale. If we knew for sure that inode was not dirty locally we could do this */ rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess, create_options, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* * Can not refresh inode by passing in file_info buf to be returned by * CIFSSMBOpen and then calling get_inode_info with returned buf since * file might have write behind data that needs to be flushed and server * version of file size can be stale. If we knew for sure that inode was * not dirty locally we could do this. */ rc = server->ops->open(xid, tcon, full_path, disposition, desired_access, create_options, &fid, &oplock, NULL, cifs_sb); if (rc) { mutex_unlock(&pCifsFile->fh_mutex); cFYI(1, "cifs_open returned 0x%x", rc); mutex_unlock(&cfile->fh_mutex); cFYI(1, "cifs_reopen returned 0x%x", rc); cFYI(1, "oplock: %d", oplock); goto reopen_error_exit; } reopen_success: pCifsFile->fid.netfid = netfid; pCifsFile->invalidHandle = false; mutex_unlock(&pCifsFile->fh_mutex); pCifsInode = CIFS_I(inode); cfile->invalidHandle = false; mutex_unlock(&cfile->fh_mutex); cinode = CIFS_I(inode); if (can_flush) { rc = filemap_write_and_wait(inode->i_mapping); mapping_set_error(inode->i_mapping, rc); if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, xid); rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb, xid, NULL); } /* else we are writing out data to server already and could deadlock if we tried to flush data, and since we do not know if we have data that would invalidate the current end of file on the server we can not go to the server to get the new inod info */ cifs_set_oplock_level(pCifsInode, oplock); cifs_relock_file(pCifsFile); rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb, xid, NULL); } /* * Else we are writing out data to server already and could deadlock if * we tried to flush data, and since we do not know if we have data that * would invalidate the current end of file on the server we can not go * to the server to get the new inode info. */ server->ops->set_fid(cfile, &fid, oplock); cifs_relock_file(cfile); reopen_error_exit: kfree(full_path); Loading