exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / getumask.c
blob49928ff822f8c2a645fd37ddc4c011ad9f748afe
1 /* Retrieve the umask of the process (multithread-safe).
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2020. */
19 /* There are three ways to implement a getumask() function on systems that
20 don't have it:
21 (a) Through system calls on the file system.
22 (b) Through a global variable that the main() function has to set,
23 together with an override of the umask() function.
24 (c) Through { mode_t mask = umask (0); umask (mask); }.
26 Each has its drawbacks:
27 (a) Causes additional system calls. May fail in some rare cases.
28 (b) Causes globally visible code complexity / maintainer effort.
29 (c) Is not multithread-safe: open() calls in other threads may
30 create files with wrong access permissions.
32 Here we implement (a), as the least evil. */
34 #include <config.h>
35 /* The package may define ASSUME_UMASK_CONSTANT to 1, to indicate that the
36 program does not call umask(). */
37 /* #define ASSUME_UMASK_CONSTANT 1 */
39 /* Specification. */
40 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
47 #include "clean-temp.h"
48 #include "tempname.h"
50 mode_t
51 getumask (void)
53 #if 0
54 /* This is not multithread-safe! */
55 mode_t mask = umask (0);
56 umask (mask);
57 return mask;
58 #else
59 # if ASSUME_UMASK_CONSTANT
60 static int cached_umask = -1;
61 if (cached_umask >= 0)
62 return cached_umask;
63 # endif
65 int mask = -1;
66 # if defined __linux__
68 /* In Linux >= 4.7, the umask can be retrieved from an "Umask:" line in the
69 /proc/self/status file. */
70 char buf[4096];
71 int fd = open ("/proc/self/status", O_RDONLY);
72 if (fd >= 0)
74 ssize_t n = read (fd, buf, sizeof (buf));
75 if (n > 0)
77 const char *p_end = buf + n;
78 const char *p = buf;
80 for (;;)
82 /* Here we're at the beginning of a line. */
83 if (p_end - p > 8 && memcmp (p, "Umask:\t0", 8) == 0)
85 unsigned int value = 0;
86 p += 8;
87 for (; p < p_end && *p >= '0' && *p <= '7'; p++)
88 value = 8 * value + (*p - '0');
89 if (p < p_end && *p == '\n')
90 mask = value;
91 break;
93 /* Search the start of the next line. */
94 for (; p < p_end && *p != '\n'; p++)
96 if (p == p_end)
97 break;
98 p++;
101 close (fd);
104 # endif
105 if (mask < 0)
107 /* Create a temporary file and inspect its access permissions. */
108 const char *tmpdir = getenv ("TMPDIR");
109 # if defined _WIN32 && !defined __CYGWIN__
110 if (tmpdir == NULL || *tmpdir == '\0')
112 /* On native Windows, TMPDIR is typically not set, and /tmp does not
113 exist. $TMP and $TEMP can be used instead. */
114 tmpdir = getenv ("TMP");
115 if (tmpdir == NULL || *tmpdir == '\0')
116 tmpdir = getenv ("TEMP");
118 # endif
119 if (tmpdir == NULL || *tmpdir == '\0')
120 tmpdir = "/tmp";
121 size_t tmpdir_length = strlen (tmpdir);
122 char *temp_filename = (char *) malloc (tmpdir_length + 15 + 1);
123 if (temp_filename != NULL)
125 memcpy (temp_filename, tmpdir, tmpdir_length);
126 strcpy (temp_filename + tmpdir_length, "/gtumask.XXXXXX");
127 int fd = gen_register_open_temp (temp_filename, 0, O_RDWR,
128 S_IRWXU|S_IRWXG|S_IRWXO);
129 if (fd >= 0)
131 struct stat statbuf;
132 if (fstat (fd, &statbuf) >= 0)
133 mask = (S_IRWXU|S_IRWXG|S_IRWXO) & ~statbuf.st_mode;
134 close_temp (fd);
135 cleanup_temporary_file (temp_filename, false);
137 free (temp_filename);
140 if (mask < 0)
142 /* We still don't know! Assume a paranoid user. */
143 mask = 077;
145 # if ASSUME_UMASK_CONSTANT
146 cached_umask = mask;
147 # endif
148 return mask;
149 #endif