NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/49687: Unexpected truncation of file occurs in open(2) when O_EXLOCK|O_TRUNC specified.
>Number: 49687
>Category: kern
>Synopsis: Unexpected truncation of file occurs in open(2) when O_EXLOCK|O_TRUNC specified.
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Feb 23 08:25:00 +0000 2015
>Originator: Hiroki Suenaga
>Release: NetBSD-current(7.99.5)
>Organization:
Internet Initiative Japan Inc.
>Environment:
NetBSD netbsd2.vmware 7.99.5 NetBSD 7.99.5 (GENERIC) #0: Mon Feb 23 07:25:43 UTC 2015 hsuenaga@netbsd2.vmware:/usr/src/sys/arch/amd64/compile/GENERIC amd64
>Description:
If open(2) is called with both of O_EXLOCK(or O_SHLOCK) and O_TRUNC flags specified and it failed to obtain a lock, the file should not be truncated though O_EXLOCK and O_SHLOCK are non-standard extension, and there is no definite specifications.
But, on current NetBSD system, the file is always truncated even if lock operation is failed. This behavior should be fixed.
>How-To-Repeat:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int fd0, fd1;
char buf[] = { 't', 'e', 's', 't', '\n'};
char buf2[] = {'\0', '\0', '\0', '\0', '\0'};
char *fname = "./file.txt";
int flag = O_RDWR|O_TRUNC|O_EXLOCK|O_CREAT|O_NONBLOCK;
if ((fd0 = open(fname, flag, 0644)) < 0) {
perror("1st descriptor");
exit(EXIT_FAILURE);
}
if (write(fd0, buf, sizeof(buf)) < 0) {
perror("write");
exit(EXIT_FAILURE);
}
if ((fd1 = open(fname, flag, 0644)) >= 0) {
printf("O_EXLOCK is not working");
exit(EXIT_FAILURE);
}
if (lseek(fd0, 0, SEEK_SET) < 0) {
perror("lseek");
exit(EXIT_FAILURE);
}
if (read(fd0, buf2, sizeof(buf2)) < 0) {
perror("read");
exit(EXIT_FAILURE);
}
if (memcmp(buf, buf2, sizeof(buf)) != 0) {
printf("File is corrupted.\n");
exit(EXIT_FAILURE);
}
printf("OK.\n");
exit(EXIT_SUCCESS);
}
The open(2) of fd1 will be fail because a lock is already held by fd0. In this case, the file "file.txt" should not be truncated and it should keep the data 'test¥n.' But file.txt is truncated after open(2) of fd1.
>Fix:
OpenBSD has fixed the problem. Here is a patch that modified for NetBSD.
Index: vfs_syscalls.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.493
diff -u -w -p -r1.493 vfs_syscalls.c
--- vfs_syscalls.c 15 Feb 2015 10:48:21 -0000 1.493
+++ vfs_syscalls.c 23 Feb 2015 08:16:59 -0000
@@ -1553,8 +1553,10 @@ do_open(lwp_t *l, struct vnode *dvp, str
struct cwdinfo *cwdi = p->p_cwdi;
file_t *fp;
struct vnode *vp;
+ struct vattr vattr;
int flags, cmode;
int indx, error;
+ int localtrunc = 0;
struct nameidata nd;
if (open_flags & O_SEARCH) {
@@ -1564,6 +1566,10 @@ do_open(lwp_t *l, struct vnode *dvp, str
flags = FFLAGS(open_flags);
if ((flags & (FREAD | FWRITE)) == 0)
return EINVAL;
+ if ((flags & O_TRUNC) && (flags & (O_EXLOCK|O_SHLOCK))) {
+ localtrunc = 1;
+ flags &= ~O_TRUNC;
+ }
if ((error = fd_allocfile(&fp, &indx)) != 0) {
return error;
@@ -1596,6 +1602,25 @@ do_open(lwp_t *l, struct vnode *dvp, str
if ((error = open_setfp(l, fp, vp, indx, flags)))
return error;
+ if (localtrunc) {
+ if ((fp->f_flag & FWRITE) == 0)
+ error = EACCES;
+ else if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ error = EROFS;
+ else if (vp->v_type == VDIR)
+ error = EISDIR;
+ else if ((error = vn_writechk(vp)) == 0) {
+ vattr_null(&vattr);
+ vattr.va_size = 0;
+ error = VOP_SETATTR(vp, &vattr, fp->f_cred);
+ }
+ if (error) {
+ VOP_UNLOCK(vp);
+ closef(fp);
+ return error;
+ }
+ }
+
VOP_UNLOCK(vp);
*fd = indx;
fd_affix(p, fp, indx);
Home |
Main Index |
Thread Index |
Old Index