151 lines
4.7 KiB
C
151 lines
4.7 KiB
C
/* Retrieve the umask of the process (multithread-safe).
|
|
Copyright (C) 2020-2024 Free Software Foundation, Inc.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
/* Written by Bruno Haible <bruno@clisp.org>, 2020. */
|
|
|
|
/* There are three ways to implement a getumask() function on systems that
|
|
don't have it:
|
|
(a) Through system calls on the file system.
|
|
(b) Through a global variable that the main() function has to set,
|
|
together with an override of the umask() function.
|
|
(c) Through { mode_t mask = umask (0); umask (mask); }.
|
|
|
|
Each has its drawbacks:
|
|
(a) Causes additional system calls. May fail in some rare cases.
|
|
(b) Causes globally visible code complexity / maintainer effort.
|
|
(c) Is not multithread-safe: open() calls in other threads may
|
|
create files with wrong access permissions.
|
|
|
|
Here we implement (a), as the least evil. */
|
|
|
|
#include <config.h>
|
|
/* The package may define ASSUME_UMASK_CONSTANT to 1, to indicate that the
|
|
program does not call umask(). */
|
|
/* #define ASSUME_UMASK_CONSTANT 1 */
|
|
|
|
/* Specification. */
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "clean-temp.h"
|
|
#include "tempname.h"
|
|
|
|
mode_t
|
|
getumask (void)
|
|
{
|
|
#if 0
|
|
/* This is not multithread-safe! */
|
|
mode_t mask = umask (0);
|
|
umask (mask);
|
|
return mask;
|
|
#else
|
|
# if ASSUME_UMASK_CONSTANT
|
|
static int cached_umask = -1;
|
|
if (cached_umask >= 0)
|
|
return cached_umask;
|
|
# endif
|
|
|
|
int mask = -1;
|
|
# if defined __linux__
|
|
{
|
|
/* In Linux >= 4.7, the umask can be retrieved from an "Umask:" line in the
|
|
/proc/self/status file. */
|
|
char buf[4096];
|
|
int fd = open ("/proc/self/status", O_RDONLY);
|
|
if (fd >= 0)
|
|
{
|
|
ssize_t n = read (fd, buf, sizeof (buf));
|
|
if (n > 0)
|
|
{
|
|
const char *p_end = buf + n;
|
|
const char *p = buf;
|
|
|
|
for (;;)
|
|
{
|
|
/* Here we're at the beginning of a line. */
|
|
if (p_end - p > 8 && memcmp (p, "Umask:\t0", 8) == 0)
|
|
{
|
|
unsigned int value = 0;
|
|
p += 8;
|
|
for (; p < p_end && *p >= '0' && *p <= '7'; p++)
|
|
value = 8 * value + (*p - '0');
|
|
if (p < p_end && *p == '\n')
|
|
mask = value;
|
|
break;
|
|
}
|
|
/* Search the start of the next line. */
|
|
for (; p < p_end && *p != '\n'; p++)
|
|
;
|
|
if (p == p_end)
|
|
break;
|
|
p++;
|
|
}
|
|
}
|
|
close (fd);
|
|
}
|
|
}
|
|
# endif
|
|
if (mask < 0)
|
|
{
|
|
/* Create a temporary file and inspect its access permissions. */
|
|
const char *tmpdir = getenv ("TMPDIR");
|
|
# if defined _WIN32 && !defined __CYGWIN__
|
|
if (tmpdir == NULL || *tmpdir == '\0')
|
|
{
|
|
/* On native Windows, TMPDIR is typically not set, and /tmp does not
|
|
exist. $TMP and $TEMP can be used instead. */
|
|
tmpdir = getenv ("TMP");
|
|
if (tmpdir == NULL || *tmpdir == '\0')
|
|
tmpdir = getenv ("TEMP");
|
|
}
|
|
# endif
|
|
if (tmpdir == NULL || *tmpdir == '\0')
|
|
tmpdir = "/tmp";
|
|
size_t tmpdir_length = strlen (tmpdir);
|
|
char *temp_filename = (char *) malloc (tmpdir_length + 15 + 1);
|
|
if (temp_filename != NULL)
|
|
{
|
|
memcpy (temp_filename, tmpdir, tmpdir_length);
|
|
strcpy (temp_filename + tmpdir_length, "/gtumask.XXXXXX");
|
|
int fd = gen_register_open_temp (temp_filename, 0, O_RDWR,
|
|
S_IRWXU|S_IRWXG|S_IRWXO);
|
|
if (fd >= 0)
|
|
{
|
|
struct stat statbuf;
|
|
if (fstat (fd, &statbuf) >= 0)
|
|
mask = (S_IRWXU|S_IRWXG|S_IRWXO) & ~statbuf.st_mode;
|
|
close_temp (fd);
|
|
cleanup_temporary_file (temp_filename, false);
|
|
}
|
|
free (temp_filename);
|
|
}
|
|
}
|
|
if (mask < 0)
|
|
{
|
|
/* We still don't know! Assume a paranoid user. */
|
|
mask = 077;
|
|
}
|
|
# if ASSUME_UMASK_CONSTANT
|
|
cached_umask = mask;
|
|
# endif
|
|
return mask;
|
|
#endif
|
|
}
|