Detab
[AROS.git] / compiler / clib / flock.c
blob993ba87e8ccc79e39ba9159a045d7330980da05a
1 /*
2 Copyright © 2008-2012, The AROS Development Team. All rights reserved.
3 $Id$
5 4.4BSD function flock().
6 */
8 #define DEBUG 0
9 #include <proto/exec.h>
10 #include <exec/exec.h>
11 #include <proto/dos.h>
12 #include <dos/dos.h>
13 #include <aros/debug.h>
14 #include <aros/symbolsets.h>
16 #include <errno.h>
18 #include "__fdesc.h"
19 #include "__arosc_privdata.h"
21 struct FlockNode
23 struct Node node;
24 struct SignalSemaphore *sem;
27 LONG AddToList(struct SignalSemaphore *sem);
28 void RemoveFromList(struct SignalSemaphore *sem);
30 /*****************************************************************************
32 NAME */
33 #include <sys/file.h>
35 int flock (
37 /* SYNOPSIS */
38 int fd,
39 int operation)
41 /* FUNCTION
42 Apply or remove an advisory lock on open file descriptor fd. Operation
43 argument can be one of the following constants:
45 LOCK_SH - Place a shared lock on the file specified by fd. More that
46 one process can hold a shared lock on a given file at a
47 time.
49 LOCK_EX - Place an exclusive lock on the file specified by fd. Only
50 one process can hold an exclusive lock on a given file at
51 a time.
53 LOCK_UN - Remove an existing lock from the file specified by fd.
55 LOCK_EX operation blocks if there is a lock already placed on the
56 file. LOCK_SH blocks if there is an exclusive lock already placed
57 on the file. If you want to do a non-blocking request, OR the
58 operation specifier with LOCK_NB constant. In this case flock() will
59 return -1 instead of blocking and set errno to EWOULDBLOCK.
61 Advisory locks created with flock() are shared among duplicated file
62 descriptors.
64 INPUTS
65 fd - File descriptor of the file you want to place or remove lock from.
66 operation - Lock operation to be performed.
68 RESULT
69 0 on success, -1 on error. In case of error a global errno variable
70 is set.
72 NOTES
73 Locks placed with flock() are only advisory, they place no
74 restrictions to any file or file descriptor operations.
76 EXAMPLE
78 BUGS
79 It's currently possible to remove lock placed by another process.
81 SEE ALSO
83 INTERNALS
84 Since advisory locks semantics is equal to exec.library semaphores
85 semantics, semaphores are used to implement locks. For a given file
86 a semaphore named FLOCK(path) is created where path is a full path to
87 the file. Locks held by a given process are stored on aroscbase->acb_file_locks
88 and released during process exit.
90 ******************************************************************************/
92 fdesc *fdesc = __getfdesc(fd);
93 char *buffer;
94 int buffersize = 256;
95 struct SignalSemaphore *sem;
97 D(bug("flock(%d, %d)\n", fd, operation));
98 if (!fdesc)
100 errno = EBADF;
101 return -1;
104 /* Sanity check */
106 (operation & ~LOCK_NB) != LOCK_SH &&
107 (operation & ~LOCK_NB) != LOCK_EX &&
108 (operation & ~LOCK_NB) != LOCK_UN
111 errno = EINVAL;
112 return -1;
115 /* Get the full path of the flocked filesystem object */
118 if(!(buffer = AllocVec(buffersize, MEMF_ANY)))
120 errno = __arosc_ioerr2errno(IoErr());
121 return -1;
124 if(NameFromFH(fdesc->fcb->fh, (STRPTR) ((IPTR) buffer + 6), buffersize - 7))
125 break;
126 else if(IoErr() != ERROR_LINE_TOO_LONG)
128 errno = __arosc_ioerr2errno(IoErr());
129 FreeVec(buffer);
130 return -1;
132 FreeVec(buffer);
133 buffersize *= 2;
135 while(TRUE);
137 CopyMem("FLOCK(", buffer, strlen("FLOCK("));
138 buffer[strlen(buffer) + 1] = '\0';
139 buffer[strlen(buffer)] = ')';
141 D(bug("[flock] Semaphore name: %s\n", buffer));
143 /* Find semaphore named FLOCK(path), add a new one if not found any */
144 Forbid();
145 sem = FindSemaphore((STRPTR) buffer);
146 if(!sem)
148 D(bug("[flock] Semaphore %s not found, creating a new one\n", buffer));
149 sem = (struct SignalSemaphore*)
150 AllocVec(sizeof(struct SignalSemaphore), MEMF_PUBLIC|MEMF_CLEAR);
151 if(!sem)
153 errno = ENOMEM;
154 Permit();
155 return -1;
157 sem->ss_Link.ln_Name = buffer;
158 AddSemaphore(sem);
160 else
162 D(bug("[flock] Semaphore %s found, freeing buffer\n", buffer));
163 FreeVec(buffer);
166 if(operation & LOCK_UN)
168 D(bug("[flock] Releasing semaphore %s\n", sem->ss_Link.ln_Name));
169 ReleaseSemaphore(sem);
170 RemoveFromList(sem);
171 if(sem->ss_Owner == NULL && sem->ss_QueueCount == -1)
173 D(bug("[flock] All locks unlocked, removing semaphore %s\n", sem->ss_Link.ln_Name));
174 /* All locks for this file were unlocked, we don't need semaphore
175 * anymore */
176 RemSemaphore(sem);
177 FreeVec(sem->ss_Link.ln_Name);
178 FreeVec(sem);
179 Permit();
180 return 0;
183 Permit();
185 switch(operation & ~LOCK_NB)
187 case LOCK_SH:
188 D(bug("[flock] Obtaining shared lock\n"));
189 if(operation & LOCK_NB)
191 if(!AttemptSemaphoreShared(sem))
193 errno = EWOULDBLOCK;
194 return -1;
197 else
198 ObtainSemaphoreShared(sem);
199 D(bug("[flock] Shared lock obtained\n"));
200 AddToList(sem);
201 break;
202 case LOCK_EX:
203 D(bug("[flock] Obtaining exclusive lock\n"));
204 if(operation & LOCK_NB)
206 if(!AttemptSemaphore(sem))
208 errno = EWOULDBLOCK;
209 return -1;
212 else
213 ObtainSemaphore(sem);
214 D(bug("[flock] Exclusive lock obtained\n"));
215 AddToList(sem);
216 break;
218 return 0;
221 LONG AddToList(struct SignalSemaphore *sem)
223 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
224 struct FlockNode *node;
225 node = AllocMem(sizeof(struct FlockNode), MEMF_ANY | MEMF_CLEAR);
226 if(!node)
227 return -1;
228 node->sem = sem;
229 AddHead(aroscbase->acb_file_locks, (struct Node*) node);
230 return 0;
233 void RemoveFromList(struct SignalSemaphore *sem)
235 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
236 struct FlockNode *varNode;
237 struct Node *tmpNode;
239 ForeachNodeSafe(aroscbase->acb_file_locks, varNode, tmpNode)
241 if(varNode->sem == sem)
243 Remove((struct Node*) varNode);
244 FreeMem(varNode, sizeof(struct FlockNode));
245 break;
250 /* __init_flocks is called during library init.
251 aroscbase->acb_file_locks will be initialized here and then copied
252 in all libbases for each open of the library.
253 This means that a global file_locks list is used, this is needed as flocks
254 are used for locking between different processes.
256 int __init_flocks(struct aroscbase *base)
258 base->acb_file_locks = AllocMem(sizeof(struct MinList), MEMF_PUBLIC);
259 NEWLIST(base->acb_file_locks);
261 D(bug("[flock] Initialized lock list at 0x%p\n", base->acb_file_locks));
263 return 1;
266 /* This function is called once before root libbase would be freed.
267 This function will be called when no other program has arosc.library open
268 so no protection should be needed.
270 void __unlock_flocks(struct aroscbase *base)
272 struct FlockNode *lock;
273 struct SignalSemaphore *sem;
275 D(bug("[flock] Freeing lock list at 0x%p\n", base->acb_file_locks));
277 while ((lock = (struct FlockNode *) REMHEAD(base->acb_file_locks)))
279 sem = lock->sem;
280 ReleaseSemaphore(sem);
281 FreeMem(lock, sizeof(struct FlockNode));
283 if (sem->ss_Owner != NULL || sem->ss_QueueCount != -1)
284 bug("[flock] Freeing locked semaphore!");
286 D(bug("[flock] removing semaphore %s\n", sem->ss_Link.ln_Name));
287 RemSemaphore(sem);
288 FreeVec(sem->ss_Link.ln_Name);
289 FreeVec(sem);
291 FreeMem(base->acb_file_locks, sizeof(struct MinList));
292 base->acb_file_locks = NULL;
295 ADD2INITLIB(__init_flocks, 1);
296 ADD2EXPUNGELIB(__unlock_flocks, 1);