1 /*	$NetBSD: myflock.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	myflock 3
6 /* SUMMARY
7 /*	lock open file
8 /* SYNOPSIS
9 /*	#include <myflock.h>
10 /*
11 /*	int	myflock(fd, lock_style, operation)
12 /*	int	fd;
13 /*	int	lock_style;
14 /*	int	operation;
15 /* DESCRIPTION
16 /*	myflock() locks or unlocks an entire open file.
17 /*
18 /*	In the case of a blocking request, a call that fails due to
19 /*	forseeable transient problems is retried once per second.
20 /*
21 /*	Arguments:
22 /* .IP fd
23 /*	The open file to be locked/unlocked.
24 /* .IP lock_style
25 /*	One of the following values:
26 /* .RS
27 /* .IP	MYFLOCK_STYLE_FLOCK
28 /*	Use BSD-style flock() locking.
29 /* .IP	MYFLOCK_STYLE_FCNTL
30 /*	Use POSIX-style fcntl() locking.
31 /* .RE
32 /* .IP operation
33 /*	One of the following values:
34 /* .RS
35 /* .IP	MYFLOCK_OP_NONE
36 /*	Release any locks the process has on the specified open file.
37 /* .IP	MYFLOCK_OP_SHARED
38 /*	Attempt to acquire a shared lock on the specified open file.
39 /*	This is appropriate for read-only access.
40 /* .IP	MYFLOCK_OP_EXCLUSIVE
41 /*	Attempt to acquire an exclusive lock on the specified open
42 /*	file. This is appropriate for write access.
43 /* .PP
44 /*	In addition, setting the MYFLOCK_OP_NOWAIT bit causes the
45 /*	call to return immediately when the requested lock cannot
46 /*	be acquired.
47 /* .RE
48 /* DIAGNOSTICS
49 /*	myflock() returns 0 in case of success, -1 in case of failure.
50 /*	A problem description is returned via the global \fIerrno\fR
51 /*	variable. In the case of a non-blocking lock request the value
52 /*	EAGAIN means that a lock is claimed by someone else.
53 /*
54 /*	Panic: attempts to use an unsupported file locking method or
55 /*	to implement an unsupported operation.
56 /* LICENSE
57 /* .ad
58 /* .fi
59 /*	The Secure Mailer license must be distributed with this software.
60 /* AUTHOR(S)
61 /*	Wietse Venema
62 /*	IBM T.J. Watson Research
63 /*	P.O. Box 704
64 /*	Yorktown Heights, NY 10598, USA
65 /*--*/
66 
67 /* System library. */
68 
69 #include "sys_defs.h"
70 #include <errno.h>
71 #include <unistd.h>
72 
73 #ifdef HAS_FCNTL_LOCK
74 #include <fcntl.h>
75 #include <string.h>
76 #endif
77 
78 #ifdef HAS_FLOCK_LOCK
79 #include <sys/file.h>
80 #endif
81 
82 /* Utility library. */
83 
84 #include "msg.h"
85 #include "myflock.h"
86 
87 /* myflock - lock/unlock entire open file */
88 
89 int     myflock(int fd, int lock_style, int operation)
90 {
91     int     status;
92 
93     /*
94      * Sanity check.
95      */
96     if ((operation & (MYFLOCK_OP_BITS)) != operation)
97 	msg_panic("myflock: improper operation type: 0x%x", operation);
98 
99     switch (lock_style) {
100 
101 	/*
102 	 * flock() does exactly what we need. Too bad it is not standard.
103 	 */
104 #ifdef HAS_FLOCK_LOCK
105     case MYFLOCK_STYLE_FLOCK:
106 	{
107 	    static int lock_ops[] = {
108 		LOCK_UN, LOCK_SH, LOCK_EX, -1,
109 		-1, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, -1
110 	    };
111 
112 	    while ((status = flock(fd, lock_ops[operation])) < 0
113 		   && errno == EINTR)
114 		sleep(1);
115 	    break;
116 	}
117 #endif
118 
119 	/*
120 	 * fcntl() is standard and does more than we need, but we can handle
121 	 * it.
122 	 */
123 #ifdef HAS_FCNTL_LOCK
124     case MYFLOCK_STYLE_FCNTL:
125 	{
126 	    struct flock lock;
127 	    int     request;
128 	    static int lock_ops[] = {
129 		F_UNLCK, F_RDLCK, F_WRLCK
130 	    };
131 
132 	    memset((char *) &lock, 0, sizeof(lock));
133 	    lock.l_type = lock_ops[operation & ~MYFLOCK_OP_NOWAIT];
134 	    request = (operation & MYFLOCK_OP_NOWAIT) ? F_SETLK : F_SETLKW;
135 	    while ((status = fcntl(fd, request, &lock)) < 0
136 		   && errno == EINTR)
137 		sleep(1);
138 	    break;
139 	}
140 #endif
141     default:
142 	msg_panic("myflock: unsupported lock style: 0x%x", lock_style);
143     }
144 
145     /*
146      * Return a consistent result. Some systems return EACCES when a lock is
147      * taken by someone else, and that would complicate error processing.
148      */
149     if (status < 0 && (operation & MYFLOCK_OP_NOWAIT) != 0)
150 	if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EACCES)
151 	    errno = EAGAIN;
152 
153     return (status);
154 }
155