diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsproto.h | 3 | ||||
-rw-r--r-- | fs/cifs/dir.c | 6 | ||||
-rw-r--r-- | fs/cifs/file.c | 57 |
3 files changed, 58 insertions, 8 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 596fc8689371..a069e7bfd2d0 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -92,6 +92,9 @@ extern u64 cifs_UnixTimeToNT(struct timespec); | |||
92 | extern __le64 cnvrtDosCifsTm(__u16 date, __u16 time); | 92 | extern __le64 cnvrtDosCifsTm(__u16 date, __u16 time); |
93 | extern struct timespec cnvrtDosUnixTm(__u16 date, __u16 time); | 93 | extern struct timespec cnvrtDosUnixTm(__u16 date, __u16 time); |
94 | 94 | ||
95 | extern int cifs_posix_open(char *full_path, struct inode **pinode, | ||
96 | struct super_block *sb, int mode, int oflags, | ||
97 | int *poplock, __u16 *pnetfid, int xid); | ||
95 | extern void posix_fill_in_inode(struct inode *tmp_inode, | 98 | extern void posix_fill_in_inode(struct inode *tmp_inode, |
96 | FILE_UNIX_BASIC_INFO *pData, int isNewInode); | 99 | FILE_UNIX_BASIC_INFO *pData, int isNewInode); |
97 | extern struct inode *cifs_new_inode(struct super_block *sb, __u64 *inum); | 100 | extern struct inode *cifs_new_inode(struct super_block *sb, __u64 *inum); |
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 89fb72832652..f9b6f68be976 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c | |||
@@ -129,7 +129,7 @@ cifs_bp_rename_retry: | |||
129 | return full_path; | 129 | return full_path; |
130 | } | 130 | } |
131 | 131 | ||
132 | static int cifs_posix_open(char *full_path, struct inode **pinode, | 132 | int cifs_posix_open(char *full_path, struct inode **pinode, |
133 | struct super_block *sb, int mode, int oflags, | 133 | struct super_block *sb, int mode, int oflags, |
134 | int *poplock, __u16 *pnetfid, int xid) | 134 | int *poplock, __u16 *pnetfid, int xid) |
135 | { | 135 | { |
@@ -187,7 +187,9 @@ static int cifs_posix_open(char *full_path, struct inode **pinode, | |||
187 | if (!pinode) | 187 | if (!pinode) |
188 | goto posix_open_ret; /* caller does not need info */ | 188 | goto posix_open_ret; /* caller does not need info */ |
189 | 189 | ||
190 | *pinode = cifs_new_inode(sb, &presp_data->UniqueId); | 190 | if (*pinode == NULL) |
191 | *pinode = cifs_new_inode(sb, &presp_data->UniqueId); | ||
192 | /* else an inode was passed in. Update its info, don't create one */ | ||
191 | 193 | ||
192 | /* We do not need to close the file if new_inode fails since | 194 | /* We do not need to close the file if new_inode fails since |
193 | the caller will retry qpathinfo as long as inode is null */ | 195 | the caller will retry qpathinfo as long as inode is null */ |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 6411f5f65d72..6603cb4024fb 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -78,8 +78,36 @@ static inline int cifs_convert_flags(unsigned int flags) | |||
78 | return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | | 78 | return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | |
79 | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | | 79 | FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | |
80 | FILE_READ_DATA); | 80 | FILE_READ_DATA); |
81 | } | ||
81 | 82 | ||
83 | static inline fmode_t cifs_posix_convert_flags(unsigned int flags) | ||
84 | { | ||
85 | fmode_t posix_flags = 0; | ||
82 | 86 | ||
87 | if ((flags & O_ACCMODE) == O_RDONLY) | ||
88 | posix_flags = FMODE_READ; | ||
89 | else if ((flags & O_ACCMODE) == O_WRONLY) | ||
90 | posix_flags = FMODE_WRITE; | ||
91 | else if ((flags & O_ACCMODE) == O_RDWR) { | ||
92 | /* GENERIC_ALL is too much permission to request | ||
93 | can cause unnecessary access denied on create */ | ||
94 | /* return GENERIC_ALL; */ | ||
95 | posix_flags = FMODE_READ | FMODE_WRITE; | ||
96 | } | ||
97 | /* can not map O_CREAT or O_EXCL or O_TRUNC flags when | ||
98 | reopening a file. They had their effect on the original open */ | ||
99 | if (flags & O_APPEND) | ||
100 | posix_flags |= (fmode_t)O_APPEND; | ||
101 | if (flags & O_SYNC) | ||
102 | posix_flags |= (fmode_t)O_SYNC; | ||
103 | if (flags & O_DIRECTORY) | ||
104 | posix_flags |= (fmode_t)O_DIRECTORY; | ||
105 | if (flags & O_NOFOLLOW) | ||
106 | posix_flags |= (fmode_t)O_NOFOLLOW; | ||
107 | if (flags & O_DIRECT) | ||
108 | posix_flags |= (fmode_t)O_DIRECT; | ||
109 | |||
110 | return posix_flags; | ||
83 | } | 111 | } |
84 | 112 | ||
85 | static inline int cifs_get_disposition(unsigned int flags) | 113 | static inline int cifs_get_disposition(unsigned int flags) |
@@ -349,7 +377,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) | |||
349 | int rc = -EACCES; | 377 | int rc = -EACCES; |
350 | int xid, oplock; | 378 | int xid, oplock; |
351 | struct cifs_sb_info *cifs_sb; | 379 | struct cifs_sb_info *cifs_sb; |
352 | struct cifsTconInfo *pTcon; | 380 | struct cifsTconInfo *tcon; |
353 | struct cifsFileInfo *pCifsFile; | 381 | struct cifsFileInfo *pCifsFile; |
354 | struct cifsInodeInfo *pCifsInode; | 382 | struct cifsInodeInfo *pCifsInode; |
355 | struct inode *inode; | 383 | struct inode *inode; |
@@ -387,7 +415,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) | |||
387 | } | 415 | } |
388 | 416 | ||
389 | cifs_sb = CIFS_SB(inode->i_sb); | 417 | cifs_sb = CIFS_SB(inode->i_sb); |
390 | pTcon = cifs_sb->tcon; | 418 | tcon = cifs_sb->tcon; |
391 | 419 | ||
392 | /* can not grab rename sem here because various ops, including | 420 | /* can not grab rename sem here because various ops, including |
393 | those that already have the rename sem can end up causing writepage | 421 | those that already have the rename sem can end up causing writepage |
@@ -404,20 +432,37 @@ reopen_error_exit: | |||
404 | 432 | ||
405 | cFYI(1, ("inode = 0x%p file flags 0x%x for %s", | 433 | cFYI(1, ("inode = 0x%p file flags 0x%x for %s", |
406 | inode, file->f_flags, full_path)); | 434 | inode, file->f_flags, full_path)); |
407 | desiredAccess = cifs_convert_flags(file->f_flags); | ||
408 | 435 | ||
409 | if (oplockEnabled) | 436 | if (oplockEnabled) |
410 | oplock = REQ_OPLOCK; | 437 | oplock = REQ_OPLOCK; |
411 | else | 438 | else |
412 | oplock = 0; | 439 | oplock = 0; |
413 | 440 | ||
441 | if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && | ||
442 | (CIFS_UNIX_POSIX_PATH_OPS_CAP & | ||
443 | le64_to_cpu(tcon->fsUnixInfo.Capability))) { | ||
444 | int oflags = (int) cifs_posix_convert_flags(file->f_flags); | ||
445 | /* can not refresh inode info since size could be stale */ | ||
446 | rc = cifs_posix_open(full_path, NULL, inode->i_sb, | ||
447 | cifs_sb->mnt_file_mode /* ignored */, | ||
448 | oflags, &oplock, &netfid, xid); | ||
449 | if (rc == 0) { | ||
450 | cFYI(1, ("posix reopen succeeded")); | ||
451 | goto reopen_success; | ||
452 | } | ||
453 | /* fallthrough to retry open the old way on errors, especially | ||
454 | in the reconnect path it is important to retry hard */ | ||
455 | } | ||
456 | |||
457 | desiredAccess = cifs_convert_flags(file->f_flags); | ||
458 | |||
414 | /* Can not refresh inode by passing in file_info buf to be returned | 459 | /* Can not refresh inode by passing in file_info buf to be returned |
415 | by SMBOpen and then calling get_inode_info with returned buf | 460 | by SMBOpen and then calling get_inode_info with returned buf |
416 | since file might have write behind data that needs to be flushed | 461 | since file might have write behind data that needs to be flushed |
417 | and server version of file size can be stale. If we knew for sure | 462 | and server version of file size can be stale. If we knew for sure |
418 | that inode was not dirty locally we could do this */ | 463 | that inode was not dirty locally we could do this */ |
419 | 464 | ||
420 | rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, | 465 | rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess, |
421 | CREATE_NOT_DIR, &netfid, &oplock, NULL, | 466 | CREATE_NOT_DIR, &netfid, &oplock, NULL, |
422 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | 467 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & |
423 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 468 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
@@ -426,6 +471,7 @@ reopen_error_exit: | |||
426 | cFYI(1, ("cifs_open returned 0x%x", rc)); | 471 | cFYI(1, ("cifs_open returned 0x%x", rc)); |
427 | cFYI(1, ("oplock: %d", oplock)); | 472 | cFYI(1, ("oplock: %d", oplock)); |
428 | } else { | 473 | } else { |
474 | reopen_success: | ||
429 | pCifsFile->netfid = netfid; | 475 | pCifsFile->netfid = netfid; |
430 | pCifsFile->invalidHandle = false; | 476 | pCifsFile->invalidHandle = false; |
431 | up(&pCifsFile->fh_sem); | 477 | up(&pCifsFile->fh_sem); |
@@ -439,7 +485,7 @@ reopen_error_exit: | |||
439 | go to server to get inode info */ | 485 | go to server to get inode info */ |
440 | pCifsInode->clientCanCacheAll = false; | 486 | pCifsInode->clientCanCacheAll = false; |
441 | pCifsInode->clientCanCacheRead = false; | 487 | pCifsInode->clientCanCacheRead = false; |
442 | if (pTcon->unix_ext) | 488 | if (tcon->unix_ext) |
443 | rc = cifs_get_inode_info_unix(&inode, | 489 | rc = cifs_get_inode_info_unix(&inode, |
444 | full_path, inode->i_sb, xid); | 490 | full_path, inode->i_sb, xid); |
445 | else | 491 | else |
@@ -467,7 +513,6 @@ reopen_error_exit: | |||
467 | cifs_relock_file(pCifsFile); | 513 | cifs_relock_file(pCifsFile); |
468 | } | 514 | } |
469 | } | 515 | } |
470 | |||
471 | kfree(full_path); | 516 | kfree(full_path); |
472 | FreeXid(xid); | 517 | FreeXid(xid); |
473 | return rc; | 518 | return rc; |