compiler/clib: Don't hide access to aroscbase behind a #define.
[AROS.git] / compiler / clib / flock.c
blob3b06e2685bbfb3b50aab63c56d3faf2d0e21c2f7
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/startup.h>
15 #include <aros/symbolsets.h>
17 #include <errno.h>
19 #include "__fdesc.h"
20 #include "__errno.h"
21 #include "__arosc_privdata.h"
23 struct FlockNode
25 struct Node node;
26 struct SignalSemaphore *sem;
29 LONG AddToList(struct SignalSemaphore *sem);
30 void RemoveFromList(struct SignalSemaphore *sem);
32 /*****************************************************************************
34 NAME */
35 #include <sys/file.h>
37 int flock (
39 /* SYNOPSIS */
40 int fd,
41 int operation)
43 /* FUNCTION
44 Apply or remove an advisory lock on open file descriptor fd. Operation
45 argument can be one of the following constants:
47 LOCK_SH - Place a shared lock on the file specified by fd. More that
48 one process can hold a shared lock on a given file at a
49 time.
51 LOCK_EX - Place an exclusive lock on the file specified by fd. Only
52 one process can hold an exclusive lock on a given file at
53 a time.
55 LOCK_UN - Remove an existing lock from the file specified by fd.
57 LOCK_EX operation blocks if there is a lock already placed on the
58 file. LOCK_SH blocks if there is an exclusive lock already placed
59 on the file. If you want to do a non-blocking request, OR the
60 operation specifier with LOCK_NB constant. In this case flock() will
61 return -1 instead of blocking and set errno to EWOULDBLOCK.
63 Advisory locks created with flock() are shared among duplicated file
64 descriptors.
66 INPUTS
67 fd - File descriptor of the file you want to place or remove lock from.
68 operation - Lock operation to be performed.
70 RESULT
71 0 on success, -1 on error. In case of error a global errno variable
72 is set.
74 NOTES
75 Locks placed with flock() are only advisory, they place no
76 restrictions to any file or file descriptor operations.
78 EXAMPLE
80 BUGS
81 It's currently possible to remove lock placed by another process.
83 SEE ALSO
85 INTERNALS
86 Since advisory locks semantics is equal to exec.library semaphores
87 semantics, semaphores are used to implement locks. For a given file
88 a semaphore named FLOCK(path) is created where path is a full path to
89 the file. Locks held by a given process are stored on aroscbase->acb_file_locks
90 and released during process exit.
92 ******************************************************************************/
94 fdesc *fdesc = __getfdesc(fd);
95 char *buffer;
96 int buffersize = 256;
97 struct SignalSemaphore *sem;
99 D(bug("flock(%d, %d)\n", fd, operation));
100 if (!fdesc)
102 errno = EBADF;
103 return -1;
106 /* Sanity check */
108 (operation & ~LOCK_NB) != LOCK_SH &&
109 (operation & ~LOCK_NB) != LOCK_EX &&
110 (operation & ~LOCK_NB) != LOCK_UN
113 errno = EINVAL;
114 return -1;
117 /* Get the full path of the flocked filesystem object */
120 if(!(buffer = AllocVec(buffersize, MEMF_ANY)))
122 errno = IoErr2errno(IoErr());
123 return -1;
126 if(NameFromFH(fdesc->fcb->fh, (STRPTR) ((IPTR) buffer + 6), buffersize - 7))
127 break;
128 else if(IoErr() != ERROR_LINE_TOO_LONG)
130 errno = IoErr2errno(IoErr());
131 FreeVec(buffer);
132 return -1;
134 FreeVec(buffer);
135 buffersize *= 2;
137 while(TRUE);
139 CopyMem("FLOCK(", buffer, strlen("FLOCK("));
140 buffer[strlen(buffer) + 1] = '\0';
141 buffer[strlen(buffer)] = ')';
143 D(bug("[flock] Semaphore name: %s\n", buffer));
145 /* Find semaphore named FLOCK(path), add a new one if not found any */
146 Forbid();
147 sem = FindSemaphore((STRPTR) buffer);
148 if(!sem)
150 D(bug("[flock] Semaphore %s not found, creating a new one\n", buffer));
151 sem = (struct SignalSemaphore*)
152 AllocVec(sizeof(struct SignalSemaphore), MEMF_PUBLIC|MEMF_CLEAR);
153 if(!sem)
155 errno = ENOMEM;
156 Permit();
157 return -1;
159 sem->ss_Link.ln_Name = buffer;
160 AddSemaphore(sem);
162 else
164 D(bug("[flock] Semaphore %s found, freeing buffer\n", buffer));
165 FreeVec(buffer);
168 if(operation & LOCK_UN)
170 D(bug("[flock] Releasing semaphore %s\n", sem->ss_Link.ln_Name));
171 ReleaseSemaphore(sem);
172 RemoveFromList(sem);
173 if(sem->ss_Owner == NULL && sem->ss_QueueCount == -1)
175 D(bug("[flock] All locks unlocked, removing semaphore %s\n", sem->ss_Link.ln_Name));
176 /* All locks for this file were unlocked, we don't need semaphore
177 * anymore */
178 RemSemaphore(sem);
179 FreeVec(sem->ss_Link.ln_Name);
180 FreeVec(sem);
181 Permit();
182 return 0;
185 Permit();
187 switch(operation & ~LOCK_NB)
189 case LOCK_SH:
190 D(bug("[flock] Obtaining shared lock\n"));
191 if(operation & LOCK_NB)
193 if(!AttemptSemaphoreShared(sem))
195 errno = EWOULDBLOCK;
196 return -1;
199 else
200 ObtainSemaphoreShared(sem);
201 D(bug("[flock] Shared lock obtained\n"));
202 AddToList(sem);
203 break;
204 case LOCK_EX:
205 D(bug("[flock] Obtaining exclusive lock\n"));
206 if(operation & LOCK_NB)
208 if(!AttemptSemaphore(sem))
210 errno = EWOULDBLOCK;
211 return -1;
214 else
215 ObtainSemaphore(sem);
216 D(bug("[flock] Exclusive lock obtained\n"));
217 AddToList(sem);
218 break;
220 return 0;
223 LONG AddToList(struct SignalSemaphore *sem)
225 struct aroscbase *aroscbase = __GM_GetBase();
226 struct FlockNode *node;
227 node = AllocMem(sizeof(struct FlockNode), MEMF_ANY | MEMF_CLEAR);
228 if(!node)
229 return -1;
230 node->sem = sem;
231 AddHead((struct List *)&aroscbase->acb_file_locks, (struct Node*) node);
232 return 0;
235 void RemoveFromList(struct SignalSemaphore *sem)
237 struct aroscbase *aroscbase = __GM_GetBase();
238 struct FlockNode *varNode;
239 struct Node *tmpNode;
241 ForeachNodeSafe(&aroscbase->acb_file_locks, varNode, tmpNode)
243 if(varNode->sem == sem)
245 Remove((struct Node*) varNode);
246 FreeMem(varNode, sizeof(struct FlockNode));
247 break;
252 int __init_flocks(struct aroscbase *base)
255 * This function is called once each time a new libbase is created
257 NEWLIST(&base->acb_file_locks);
259 D(bug("[flock] Initialized lock list at 0x%p\n", &base->acb_file_locks));
261 return 1;
264 void __unlock_flocks(struct aroscbase *base)
266 /* This function is called once before libbase would be freed */
267 if (base->acb_file_locks.mlh_Head)
269 struct FlockNode *lock;
270 struct SignalSemaphore *sem;
272 D(bug("[flock] Freeing lock list at 0x%p\n", &base->acb_file_locks));
274 while ((lock = (struct FlockNode *) REMHEAD(&base->acb_file_locks)))
276 sem = lock->sem;
277 ReleaseSemaphore(sem);
278 FreeMem(lock, sizeof(struct FlockNode));
280 if(sem->ss_Owner == NULL && sem->ss_QueueCount == -1)
282 D(bug("[flock] All locks unlocked, removing semaphore %s\n", sem->ss_Link.ln_Name));
283 /* All locks for this file were unlocked, we don't need semaphore
284 * anymore */
285 RemSemaphore(sem);
286 FreeVec(sem->ss_Link.ln_Name);
287 FreeVec(sem);
293 ADD2OPENLIB(__init_flocks, 1);
294 ADD2CLOSELIB(__unlock_flocks, 1);