+++ /dev/null
-/*
- * Copyright notice from original mutt:
- * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
- * Copyright (C) 1998-2000 Thomas Roessler <roessler@does-not-exist.org>
- *
- * This file is part of mutt-ng, see http://www.muttng.org/.
- * It's licensed under the GNU General Public License,
- * please see the file GPL in the top level source directory.
- */
-
-#include <lib-lib/lib-lib.h>
-
-#include <sys/utsname.h>
-#include "dotlock.h"
-
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-
-#define MAXLINKS 1024 /* maximum link depth */
-
-# define LONG_STRING 1024
-# define MAXLOCKATTEMPT 5
-
-# ifdef USE_SETGID
-
-# ifdef HAVE_SETEGID
-# define SETEGID setegid
-# else
-# define SETEGID setgid
-# endif
-# ifndef S_ISLNK
-# define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK ? 1 : 0)
-# endif
-
-# endif
-
-static int DotlockFlags;
-static int Retry = MAXLOCKATTEMPT;
-
-static char *Hostname;
-
-#ifdef USE_SETGID
-static gid_t UserGid;
-static gid_t MailGid;
-#endif
-
-static int dotlock_deference_symlink (char *, size_t, const char *);
-static int dotlock_prepare (char *, ssize_t, const char *, int fd);
-static int dotlock_check_stats (struct stat *, struct stat *);
-static int dotlock_dispatch (const char *, int fd);
-
-static int dotlock_init_privs (void);
-static void usage (const char *);
-
-static void dotlock_expand_link (char *, const char *, const char *);
-static void BEGIN_PRIVILEGED (void);
-static void END_PRIVILEGED (void);
-
-/* These functions work on the current directory.
- * Invoke dotlock_prepare () before and check their
- * return value.
- */
-
-static int dotlock_try (void);
-static int dotlock_unlock (const char *);
-static int dotlock_unlink (const char *);
-static int dotlock_lock (const char *);
-
-
-#define check_flags(a) \
- if (a & (DL_FL_TRY|DL_FL_UNLOCK|DL_FL_UNLINK)) \
- usage(argv[0])
-
-int main (int argc, char **argv)
-{
- int i;
- char *p;
- struct utsname utsname;
-
- /* first, drop privileges */
-
- if (dotlock_init_privs () == -1)
- return DL_EX_ERROR;
-
-
- /* determine the system's host name */
-
- uname (&utsname);
- if (!(Hostname = strdup(utsname.nodename)))
- return DL_EX_ERROR;
- if ((p = strchr (Hostname, '.')))
- *p = '\0';
-
-
- /* parse the command line options. */
- DotlockFlags = 0;
-
- while ((i = getopt (argc, argv, "dtfupr:")) != EOF) {
- switch (i) {
- /* actions, mutually exclusive */
- case 't':
- check_flags (DotlockFlags);
- DotlockFlags |= DL_FL_TRY;
- break;
- case 'd':
- check_flags (DotlockFlags);
- DotlockFlags |= DL_FL_UNLINK;
- break;
- case 'u':
- check_flags (DotlockFlags);
- DotlockFlags |= DL_FL_UNLOCK;
- break;
-
- /* other flags */
- case 'f':
- DotlockFlags |= DL_FL_FORCE;
- break;
- case 'p':
- DotlockFlags |= DL_FL_USEPRIV;
- break;
- case 'r':
- DotlockFlags |= DL_FL_RETRY;
- Retry = atoi (optarg);
- break;
-
- default:
- usage (argv[0]);
- }
- }
-
- if (optind == argc || Retry < 0)
- usage (argv[0]);
-
- return dotlock_dispatch (argv[optind], -1);
-}
-
-
-/*
- * Determine our effective group ID, and drop
- * privileges.
- *
- * Return value:
- *
- * 0 - everything went fine
- * -1 - we couldn't drop privileges.
- *
- */
-
-
-static int dotlock_init_privs (void)
-{
-
-# ifdef USE_SETGID
-
- UserGid = getgid ();
- MailGid = getegid ();
-
- if (SETEGID (UserGid) != 0)
- return -1;
-
-# endif
-
- return 0;
-}
-
-
-static int dotlock_dispatch (const char *f, int fd)
-{
- char frealpath[_POSIX_PATH_MAX];
-
- /* If dotlock_prepare () succeeds [return value == 0],
- * realpath contains the basename of f, and we have
- * successfully changed our working directory to
- * `dirname $f`. Additionally, f has been opened for
- * reading to verify that the user has at least read
- * permissions on that file.
- *
- * For a more detailed explanation of all this, see the
- * lengthy comment below.
- */
-
- if (dotlock_prepare (frealpath, sizeof (frealpath), f, fd) != 0)
- return DL_EX_ERROR;
-
- /* Actually perform the locking operation. */
-
- if (DotlockFlags & DL_FL_TRY)
- return dotlock_try ();
- else if (DotlockFlags & DL_FL_UNLOCK)
- return dotlock_unlock (frealpath);
- else if (DotlockFlags & DL_FL_UNLINK)
- return dotlock_unlink (frealpath);
- else /* lock */
- return dotlock_lock (frealpath);
-}
-
-
-/*
- * Get privileges
- *
- * This function re-acquires the privileges we may have
- * if the user told us to do so by giving the "-p"
- * command line option.
- *
- * BEGIN_PRIVILEGES () won't return if an error occurs.
- *
- */
-
-static void BEGIN_PRIVILEGED (void)
-{
-#ifdef USE_SETGID
- if (DotlockFlags & DL_FL_USEPRIV) {
- if (SETEGID (MailGid) != 0) {
- /* perror ("setegid"); */
- exit (DL_EX_ERROR);
- }
- }
-#endif
-}
-
-/*
- * Drop privileges
- *
- * This function drops the group privileges we may have.
- *
- * END_PRIVILEGED () won't return if an error occurs.
- *
- */
-
-static void END_PRIVILEGED (void)
-{
-#ifdef USE_SETGID
- if (DotlockFlags & DL_FL_USEPRIV) {
- if (SETEGID (UserGid) != 0) {
- /* perror ("setegid"); */
- exit (DL_EX_ERROR);
- }
- }
-#endif
-}
-
-/*
- * Usage information.
- *
- * This function doesn't return.
- *
- */
-
-static void usage (const char *av0)
-{
- fprintf (stderr, "dotlock [Madmutt %s]\n", VERSION);
- fprintf (stderr, "usage: %s [-t|-f|-u|-d] [-p] [-r <retries>] file\n", av0);
-
- fputs ("\noptions:"
- "\n -t\t\ttry"
- "\n -f\t\tforce"
- "\n -u\t\tunlock" "\n -d\t\tunlink" "\n -p\t\tprivileged"
-#ifndef USE_SETGID
- " (ignored)"
-#endif
- "\n -r <retries>\tRetry locking" "\n", stderr);
-
- exit (DL_EX_ERROR);
-}
-
-/*
- * Access checking: Let's avoid to lock other users' mail
- * spool files if we aren't permitted to read them.
- *
- * Some simple-minded access (2) checking isn't sufficient
- * here: The problem is that the user may give us a
- * deeply nested path to a file which has the same name
- * as the file he wants to lock, but different
- * permissions, say, e.g.
- * /tmp/lots/of/subdirs/var/spool/mail/root.
- *
- * He may then try to replace /tmp/lots/of/subdirs by a
- * symbolic link to / after we have invoked access () to
- * check the file's permissions. The lockfile we'd
- * create or remove would then actually be
- * /var/spool/mail/root.
- *
- * To avoid this attack, we proceed as follows:
- *
- * - First, follow symbolic links a la
- * dotlock_deference_symlink ().
- *
- * - get the result's dirname.
- *
- * - chdir to this directory. If you can't, bail out.
- *
- * - try to open the file in question, only using its
- * basename. If you can't, bail out.
- *
- * - fstat that file and compare the result to a
- * subsequent lstat (only using the basename). If
- * the comparison fails, bail out.
- *
- * dotlock_prepare () is invoked from main () directly
- * after the command line parsing has been done.
- *
- * Return values:
- *
- * 0 - Evereything's fine. The program's new current
- * directory is the contains the file to be locked.
- * The string pointed to by bn contains the name of
- * the file to be locked.
- *
- * -1 - Something failed. Don't continue.
- *
- * tlr, Jul 15 1998
- */
-
-static int dotlock_check_stats (struct stat *fsb, struct stat *lsb)
-{
- /* S_ISLNK (fsb->st_mode) should actually be impossible,
- * but we may have mixed up the parameters somewhere.
- * play safe.
- */
-
- if (S_ISLNK (lsb->st_mode) || S_ISLNK (fsb->st_mode))
- return -1;
-
- if ((lsb->st_dev != fsb->st_dev) ||
- (lsb->st_ino != fsb->st_ino) ||
- (lsb->st_mode != fsb->st_mode) ||
- (lsb->st_nlink != fsb->st_nlink) ||
- (lsb->st_uid != fsb->st_uid) ||
- (lsb->st_gid != fsb->st_gid) ||
- (lsb->st_rdev != fsb->st_rdev) || (lsb->st_size != fsb->st_size)) {
- /* something's fishy */
- return -1;
- }
-
- return 0;
-}
-
-static int dotlock_prepare (char *bn, ssize_t l, const char *f, int _fd)
-{
- struct stat fsb, lsb;
- char frealpath[_POSIX_PATH_MAX];
- char *fbasename, *dirname;
- char *p;
- int fd;
- int r;
-
- if (dotlock_deference_symlink (frealpath, sizeof (frealpath), f) == -1)
- return -1;
-
- if ((p = strrchr (frealpath, '/'))) {
- *p = '\0';
- fbasename = p + 1;
- dirname = frealpath;
- }
- else {
- fbasename = frealpath;
- dirname = m_strdup(".");
- }
-
- if (m_strlen(fbasename) + 1 > l)
- return -1;
-
- m_strcpy(bn, l, fbasename);
-
- if (chdir (dirname) == -1)
- return -1;
-
- if (_fd != -1)
- fd = _fd;
- else if ((fd = open (fbasename, O_RDONLY)) == -1)
- return -1;
-
- r = fstat (fd, &fsb);
-
- if (_fd == -1)
- close (fd);
-
- if (r == -1)
- return -1;
-
- if (lstat (fbasename, &lsb) == -1)
- return -1;
-
- if (dotlock_check_stats (&fsb, &lsb) == -1)
- return -1;
-
- return 0;
-}
-
-/*
- * Expand a symbolic link.
- *
- * This function expects newpath to have space for
- * at least _POSIX_PATH_MAX characters.
- *
- */
-
-static void
-dotlock_expand_link (char *newpath, const char *path, const char *flink)
-{
- const char *lb = NULL;
- size_t len;
-
- /* link is full path */
- if (*flink == '/') {
- m_strcpy(newpath, _POSIX_PATH_MAX, flink);
- return;
- }
-
- if ((lb = strrchr (path, '/')) == NULL) {
- /* no path in link */
- m_strcpy(newpath, _POSIX_PATH_MAX, flink);
- return;
- }
-
- len = lb - path + 1;
- memcpy (newpath, path, len);
- m_strcpy(newpath + len, _POSIX_PATH_MAX - len, flink);
-}
-
-
-/*
- * Deference a chain of symbolic links
- *
- * The final path is written to d.
- */
-static int dotlock_deference_symlink (char *d, size_t l, const char *path)
-{
- struct stat sb;
- char frealpath[_POSIX_PATH_MAX];
- const char *pathptr = path;
- int count = 0;
-
- while (count++ < MAXLINKS) {
- if (lstat (pathptr, &sb) == -1) {
- /* perror (pathptr); */
- return -1;
- }
-
- if (S_ISLNK (sb.st_mode)) {
- char linkfile[_POSIX_PATH_MAX];
- char linkpath[_POSIX_PATH_MAX];
- int len;
-
- if ((len = readlink (pathptr, linkfile, sizeof (linkfile))) == -1) {
- /* perror (pathptr); */
- return -1;
- }
-
- linkfile[len] = '\0';
- dotlock_expand_link (linkpath, pathptr, linkfile);
- m_strcpy(frealpath, sizeof(frealpath), linkpath);
- pathptr = frealpath;
- }
- else
- break;
- }
-
- m_strcpy(d, l, pathptr);
- return 0;
-}
-
-/*
- * Dotlock a file.
- *
- * realpath is assumed _not_ to be an absolute path to
- * the file we are about to lock. Invoke
- * dotlock_prepare () before using this function!
- *
- */
-
-#define HARDMAXATTEMPTS 10
-
-static int dotlock_lock (const char *frealpath)
-{
- char lockfile[_POSIX_PATH_MAX + LONG_STRING];
- char nfslockfile[_POSIX_PATH_MAX + LONG_STRING];
- ssize_t prev_size = 0;
- int fd;
- int count = 0;
- int hard_count = 0;
- struct stat sb;
- time_t t;
-
- snprintf (nfslockfile, sizeof (nfslockfile), "%s.%s.%d",
- frealpath, Hostname, (int) getpid ());
- snprintf (lockfile, sizeof (lockfile), "%s.lock", frealpath);
-
-
- BEGIN_PRIVILEGED ();
-
- unlink (nfslockfile);
-
- while ((fd = open (nfslockfile, O_WRONLY | O_EXCL | O_CREAT, 0)) < 0) {
- END_PRIVILEGED ();
-
-
- if (errno != EAGAIN) {
- /* perror ("cannot open NFS lock file"); */
- return DL_EX_ERROR;
- }
-
-
- BEGIN_PRIVILEGED ();
- }
-
- END_PRIVILEGED ();
-
-
- close (fd);
-
- while (hard_count++ < HARDMAXATTEMPTS) {
-
- BEGIN_PRIVILEGED ();
- link (nfslockfile, lockfile);
- END_PRIVILEGED ();
-
- if (stat (nfslockfile, &sb) != 0) {
- /* perror ("stat"); */
- return DL_EX_ERROR;
- }
-
- if (sb.st_nlink == 2)
- break;
-
- if (count == 0)
- prev_size = sb.st_size;
-
- if (prev_size == sb.st_size && ++count > Retry) {
- if (DotlockFlags & DL_FL_FORCE) {
- BEGIN_PRIVILEGED ();
- unlink (lockfile);
- END_PRIVILEGED ();
-
- count = 0;
- continue;
- }
- else {
- BEGIN_PRIVILEGED ();
- unlink (nfslockfile);
- END_PRIVILEGED ();
- return DL_EX_EXIST;
- }
- }
-
- prev_size = sb.st_size;
-
- /* don't trust sleep (3) as it may be interrupted
- * by users sending signals.
- */
-
- t = time (NULL);
- do {
- sleep (1);
- } while (time (NULL) == t);
- }
-
- BEGIN_PRIVILEGED ();
- unlink (nfslockfile);
- END_PRIVILEGED ();
-
- return DL_EX_OK;
-}
-
-
-/*
- * Unlock a file.
- *
- * The same comment as for dotlock_lock () applies here.
- *
- */
-
-static int dotlock_unlock (const char *frealpath)
-{
- char lockfile[_POSIX_PATH_MAX + LONG_STRING];
- int i;
-
- snprintf (lockfile, sizeof (lockfile), "%s.lock", frealpath);
-
- BEGIN_PRIVILEGED ();
- i = unlink (lockfile);
- END_PRIVILEGED ();
-
- if (i == -1)
- return DL_EX_ERROR;
-
- return DL_EX_OK;
-}
-
-/* remove an empty file */
-
-static int dotlock_unlink (const char *frealpath)
-{
- struct stat lsb;
- int i = -1;
-
- if (dotlock_lock (frealpath) != DL_EX_OK)
- return DL_EX_ERROR;
-
- if ((i = lstat (frealpath, &lsb)) == 0 && lsb.st_size == 0)
- unlink (frealpath);
-
- dotlock_unlock (frealpath);
-
- return (i == 0) ? DL_EX_OK : DL_EX_ERROR;
-}
-
-
-/*
- * Check if a file can be locked at all.
- *
- * The same comment as for dotlock_lock () applies here.
- *
- */
-
-static int dotlock_try (void)
-{
-#ifdef USE_SETGID
- struct stat sb;
-#endif
-
- if (access (".", W_OK) == 0)
- return DL_EX_OK;
-
-#ifdef USE_SETGID
- if (stat (".", &sb) == 0) {
- if ((sb.st_mode & S_IWGRP) == S_IWGRP && sb.st_gid == MailGid)
- return DL_EX_NEED_PRIVS;
- }
-#endif
-
- return DL_EX_IMPOSSIBLE;
-}
#include <lib-lib/lib-lib.h>
+#include <lockfile.h>
+
#include <lib-lua/lib-lua.h>
#include <lib-sys/unix.h>
#include <lib-mime/mime.h>
#include "keymap.h"
#include "compress.h"
#include "score.h"
-#include "dotlock.h"
#include <imap/imap.h>
#include "pop.h"
#define MX_IDX(idx) (idx >= 0 && idx < countof(mxfmts))
#define mutt_is_spool(s) (m_strcmp(Spoolfile, s) == 0)
-/* parameters:
- * path - file to lock
- * retry - should retry if unable to lock?
- */
-static int invoke_dotlock (const char *path, int flags, int retry)
+static int dotlock_file(const char *path, int retry)
{
- char cmd[LONG_STRING + _POSIX_PATH_MAX];
- char f[STRING + _POSIX_PATH_MAX];
- char r[STRING];
-
- if (flags & DL_FL_RETRY)
- snprintf (r, sizeof (r), "-r %d ", retry ? MAXLOCKATTEMPT : 0);
-
- mutt_quote_filename (f, sizeof (f), path);
-
- snprintf(cmd, sizeof(cmd), "%s %s%s%s%s%s%s%s",
- mod_core.dotlock,
- flags & DL_FL_TRY ? "-t " : "",
- flags & DL_FL_UNLOCK ? "-u " : "",
- flags & DL_FL_USEPRIV ? "-p " : "",
- flags & DL_FL_FORCE ? "-f " : "",
- flags & DL_FL_UNLINK ? "-d " : "",
- flags & DL_FL_RETRY ? r : "", f);
-
- return mutt_system (cmd);
-}
+ char lockfile[PATH_MAX];
+ snprintf(lockfile, sizeof(lockfile), "%s.lock", path);
-static int dotlock_file (const char *path, int retry)
-{
- int r;
- int flags = DL_FL_USEPRIV | DL_FL_RETRY;
-
- if (retry)
- retry = 1;
-
-retry_lock:
- if ((r = invoke_dotlock (path, flags, retry)) == DL_EX_EXIST) {
- if (!option (OPTNOCURSES)) {
- char msg[LONG_STRING];
-
- snprintf (msg, sizeof (msg),
- _("Lock count exceeded, remove lock for %s?"), path);
- if (retry && mutt_yesorno (msg, M_YES) == M_YES) {
- flags |= DL_FL_FORCE;
- retry--;
- mutt_clear_error ();
- goto retry_lock;
- }
- }
- else {
- mutt_error (_("Can't dotlock %s.\n"), path);
+ if (lockfile_create(lockfile, retry ? 1 : 0, 0)) {
+ if (retry)
+ mutt_error (_("Can't dotlock %s.\n"), lockfile);
}
- }
- return (r == DL_EX_OK ? 0 : -1);
+ return 0;
}
static int undotlock_file (const char *path)
{
- return (invoke_dotlock (path, DL_FL_USEPRIV | DL_FL_UNLOCK, 0) ==
- DL_EX_OK ? 0 : -1);
+ char lockfile[PATH_MAX];
+ snprintf(lockfile, sizeof(lockfile), "%s.lock", path);
+ lockfile_remove(lockfile);
}
/* looks up index of type for path in mxfmts */
#ifdef USE_FCNTL
struct flock lck;
-
p_clear(&lck, 1);
lck.l_type = excl ? F_WRLCK : F_RDLCK;
lck.l_whence = SEEK_SET;
#endif /* USE_FLOCK */
if (r == 0 && dot)
- r = dotlock_file (path, time_out);
+ r = dotlock_file(path, time_out);
if (r == -1) {
/* release any other locks obtained in this routine */