THANKS: Andrew Gee
[s-mailx.git] / dotlock.h
blob8a96e99f6a877f4e85740649e514cc56b3757bc6
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Creation of an exclusive "dotlock" file. This is (potentially) shared
3 *@ in between n_dotlock() and the privilege-separated "dotlocker"..
4 *@ (Which is why it doesn't use NYD or other utilities.)
5 *@ The code assumes it has been chdir(2)d into the target directory and
6 *@ that SIGPIPE is ignored (we react upon ERR_PIPE).
7 *@ It furtherly assumes that it can create a file name that is at least one
8 *@ byte longer than the dotlock file's name!
10 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
13 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 /* Jump in */
37 static enum n_dotlock_state a_dotlock_create(struct n_dotlock_info *dip);
39 /* Create a unique file. O_EXCL does not really work over NFS so we follow
40 * the following trick (inspired by S.R. van den Berg):
41 * - make a mostly unique filename and try to create it
42 * - link the unique filename to our target
43 * - get the link count of the target
44 * - unlink the mostly unique filename
45 * - if the link count was 2, then we are ok; else we've failed */
46 static enum n_dotlock_state a_dotlock__create_excl(struct n_dotlock_info *dip,
47 char const *lname);
49 static enum n_dotlock_state
50 a_dotlock_create(struct n_dotlock_info *dip){
51 /* Use PATH_MAX not NAME_MAX to catch those "we proclaim the minimum value"
52 * problems (SunOS), since the pathconf(3) value came too late! */
53 char lname[PATH_MAX +1];
54 sigset_t nset, oset;
55 size_t tries;
56 ssize_t w;
57 enum n_dotlock_state rv, xrv;
59 /* (Callee ensured this doesn't end up as plain "di_lock_name") */
60 snprintf(lname, sizeof lname, "%s.%s.%s",
61 dip->di_lock_name, dip->di_hostname, dip->di_randstr);
63 sigfillset(&nset);
65 for(tries = 0;; ++tries){
66 sigprocmask(SIG_BLOCK, &nset, &oset);
67 rv = a_dotlock__create_excl(dip, lname);
68 sigprocmask(SIG_SETMASK, &oset, NULL);
70 if(rv == n_DLS_NONE || (rv & n_DLS_ABANDON))
71 break;
72 if(dip->di_pollmsecs == 0 || tries >= DOTLOCK_TRIES){
73 rv |= n_DLS_ABANDON;
74 break;
77 xrv = n_DLS_PING;
78 w = write(STDOUT_FILENO, &xrv, sizeof xrv);
79 if(w == -1 && n_err_no == n_ERR_PIPE){
80 rv = n_DLS_DUNNO | n_DLS_ABANDON;
81 break;
83 n_msleep(dip->di_pollmsecs, FAL0);
85 return rv;
88 static enum n_dotlock_state
89 a_dotlock__create_excl(struct n_dotlock_info *dip, char const *lname){
90 struct stat stb;
91 int fd, e;
92 size_t tries;
93 enum n_dotlock_state rv = n_DLS_NONE;
95 /* We try to create the unique filename */
96 for(tries = 0;; ++tries){
97 fd = open(lname,
98 #ifdef O_SYNC
99 (O_WRONLY | O_CREAT | O_EXCL | O_SYNC),
100 #else
101 (O_WRONLY | O_CREAT | O_EXCL),
102 #endif
103 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
104 if(fd != -1){
105 #ifdef n_PRIVSEP_SOURCE
106 if(dip->di_stb != NULL &&
107 fchown(fd, dip->di_stb->st_uid, dip->di_stb->st_gid)){
108 int x = n_err_no;
109 close(fd);
110 n_err_no = x;
111 goto jbados;
113 #endif
114 close(fd);
115 break;
116 }else if((e = n_err_no) != n_ERR_EXIST){
117 rv = (e == n_ERR_ROFS) ? n_DLS_ROFS | n_DLS_ABANDON : n_DLS_NOPERM;
118 goto jleave;
119 }else if(tries >= DOTLOCK_TRIES){
120 rv = n_DLS_EXIST;
121 goto jleave;
125 /* We link the name to the fname */
126 if(link(lname, dip->di_lock_name) == -1)
127 goto jbados;
129 /* Note that we stat our own exclusively created name, not the
130 * destination, since the destination can be affected by others */
131 if(stat(lname, &stb) == -1)
132 goto jbados;
134 unlink(lname);
136 /* If the number of links was two (one for the unique file and one for
137 * the lock), we've won the race */
138 if(stb.st_nlink != 2)
139 rv = n_DLS_EXIST;
140 jleave:
141 return rv;
142 jbados:
143 rv = (n_err_no == n_ERR_EXIST) ? n_DLS_EXIST : n_DLS_NOPERM | n_DLS_ABANDON;
144 unlink(lname);
145 goto jleave;
148 /* s-it-mode */