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>.
11 * SPDX-License-Identifier: BSD-2-Clause
14 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 static enum n_dotlock_state
a_dotlock_create(struct n_dotlock_info
*dip
);
40 /* Create a unique file. O_EXCL does not really work over NFS so we follow
41 * the following trick (inspired by S.R. van den Berg):
42 * - make a mostly unique filename and try to create it
43 * - link the unique filename to our target
44 * - get the link count of the target
45 * - unlink the mostly unique filename
46 * - if the link count was 2, then we are ok; else we've failed */
47 static enum n_dotlock_state
a_dotlock__create_excl(struct n_dotlock_info
*dip
,
50 static enum n_dotlock_state
51 a_dotlock_create(struct n_dotlock_info
*dip
){
52 /* Use PATH_MAX not NAME_MAX to catch those "we proclaim the minimum value"
53 * problems (SunOS), since the pathconf(3) value came too late! */
54 char lname
[PATH_MAX
+1];
58 enum n_dotlock_state rv
, xrv
;
60 /* (Callee ensured this doesn't end up as plain "di_lock_name") */
61 snprintf(lname
, sizeof lname
, "%s.%s.%s",
62 dip
->di_randstr
, dip
->di_lock_name
, dip
->di_hostname
);
66 for(tries
= 0;; ++tries
){
67 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
68 rv
= a_dotlock__create_excl(dip
, lname
);
69 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
71 if(rv
== n_DLS_NONE
|| (rv
& n_DLS_ABANDON
))
73 if(dip
->di_pollmsecs
== 0 || tries
>= DOTLOCK_TRIES
){
79 w
= write(STDOUT_FILENO
, &xrv
, sizeof xrv
);
80 if(w
== -1 && n_err_no
== n_ERR_PIPE
){
81 rv
= n_DLS_DUNNO
| n_DLS_ABANDON
;
84 n_msleep(dip
->di_pollmsecs
, FAL0
);
89 static enum n_dotlock_state
90 a_dotlock__create_excl(struct n_dotlock_info
*dip
, char const *lname
){
94 enum n_dotlock_state rv
= n_DLS_NONE
;
96 /* We try to create the unique filename */
97 for(tries
= 0;; ++tries
){
100 (O_WRONLY
| O_CREAT
| O_EXCL
| O_SYNC
),
102 (O_WRONLY
| O_CREAT
| O_EXCL
),
104 S_IWUSR
| S_IRUSR
| S_IRGRP
| S_IROTH
);
106 #ifdef n_PRIVSEP_SOURCE
107 if(dip
->di_stb
!= NULL
&&
108 fchown(fd
, dip
->di_stb
->st_uid
, dip
->di_stb
->st_gid
)){
117 }else if((e
= n_err_no
) != n_ERR_EXIST
){
118 rv
= (e
== n_ERR_ROFS
) ? n_DLS_ROFS
| n_DLS_ABANDON
: n_DLS_NOPERM
;
120 }else if(tries
>= DOTLOCK_TRIES
){
126 /* We link the name to the fname */
127 if(link(lname
, dip
->di_lock_name
) == -1)
130 /* Note that we stat our own exclusively created name, not the
131 * destination, since the destination can be affected by others */
132 if(stat(lname
, &stb
) == -1)
137 /* If the number of links was two (one for the unique file and one for
138 * the lock), we've won the race */
139 if(stb
.st_nlink
!= 2)
144 rv
= (n_err_no
== n_ERR_EXIST
) ? n_DLS_EXIST
: n_DLS_NOPERM
| n_DLS_ABANDON
;