⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dotlock.c

📁 mutt-1.5.12 源代码。linux 下邮件接受的工具。
💻 C
字号:
/* * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org> * Copyright (C) 1998-2000 Thomas Roessler <roessler@does-not-exist.org> *  *     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 2 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, write to the Free Software *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. */ /* * This module either be compiled into Mutt, or it can be * built as a separate program. For building it * separately, define the DL_STANDALONE preprocessor * macro. */#if HAVE_CONFIG_H# include "config.h"#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <dirent.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/utsname.h>#include <errno.h>#include <time.h>#include <fcntl.h>#include <limits.h>#ifndef _POSIX_PATH_MAX#include <posix1_lim.h>#endif#include "dotlock.h"#include "config.h"#ifdef HAVE_GETOPT_H#include <getopt.h>#endif#ifdef DL_STANDALONE# include "reldate.h"#endif#define MAXLINKS 1024 /* maximum link depth */#ifdef DL_STANDALONE# define LONG_STRING 1024# define MAXLOCKATTEMPT 5# define strfcpy(A,B,C) strncpy (A,B,C), *(A+(C)-1)=0# 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# ifndef HAVE_SNPRINTFextern int snprintf (char *, size_t, const char *, ...);# endif#else  /* DL_STANDALONE */# ifdef USE_SETGID#   error Do not try to compile dotlock as a mutt module when requiring egid switching!# endif# include "mutt.h"# include "mx.h"#endif /* DL_STANDALONE */static int DotlockFlags;static int Retry = MAXLOCKATTEMPT;#ifdef DL_STANDALONEstatic char *Hostname;#endif#ifdef USE_SETGIDstatic gid_t UserGid;static gid_t MailGid;#endifstatic int dotlock_deference_symlink (char *, size_t, const char *);static int dotlock_prepare (char *, size_t, const char *, int fd);static int dotlock_check_stats (struct stat *, struct stat *);static int dotlock_dispatch (const char *, int fd);#ifdef DL_STANDALONEstatic int dotlock_init_privs (void);static void usage (const char *);#endifstatic 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 *);#ifdef DL_STANDALONE#define check_flags(a) if (a & DL_FL_ACTIONS) 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)))	/* __MEM_CHECKED__ */    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 intdotlock_init_privs (void){# ifdef USE_SETGID    UserGid = getgid ();  MailGid = getegid ();  if (SETEGID (UserGid) != 0)    return -1;# endif  return 0;}  #else  /* DL_STANDALONE *//*  * This function is intended to be invoked from within * mutt instead of mx.c's invoke_dotlock (). */int dotlock_invoke (const char *path, int fd, int flags, int retry){  int currdir;  int r;  DotlockFlags = flags;    if ((currdir = open (".", O_RDONLY)) == -1)    return DL_EX_ERROR;  if (!(DotlockFlags & DL_FL_RETRY) || retry)    Retry = MAXLOCKATTEMPT;  else    Retry = 0;    r = dotlock_dispatch (path, fd);    fchdir (currdir);  close (currdir);    return r;}#endif  /* DL_STANDALONE */static int dotlock_dispatch (const char *f, int fd){  char realpath[_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 (realpath, sizeof (realpath), 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 (realpath);  else if (DotlockFlags & DL_FL_UNLINK)    return dotlock_unlink (realpath);  else /* lock */    return dotlock_lock (realpath);}  /* * 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 voidBEGIN_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 voidEND_PRIVILEGED (void){#ifdef USE_SETGID  if (DotlockFlags & DL_FL_USEPRIV)  {    if (SETEGID (UserGid) != 0)    {      /* perror ("setegid"); */      exit (DL_EX_ERROR);    }  }#endif}#ifdef DL_STANDALONE/* * Usage information. *  * This function doesn't return. *  */static void usage (const char *av0){  fprintf (stderr, "dotlock [Mutt %s (%s)]\n", VERSION, ReleaseDate);  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);}#endif/* * 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 intdotlock_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 intdotlock_prepare (char *bn, size_t l, const char *f, int _fd){  struct stat fsb, lsb;  char realpath[_POSIX_PATH_MAX];  char *basename, *dirname;  char *p;  int fd;  int r;    if (dotlock_deference_symlink (realpath, sizeof (realpath), f) == -1)    return -1;    if ((p = strrchr (realpath, '/')))  {    *p = '\0';    basename = p + 1;    dirname = realpath;  }  else  {    basename = realpath;    dirname = ".";  }  if (strlen (basename) + 1 > l)    return -1;    strfcpy (bn, basename, l);    if (chdir (dirname) == -1)    return -1;  if (_fd != -1)    fd = _fd;  else if ((fd = open (basename, O_RDONLY)) == -1)    return -1;    r = fstat (fd, &fsb);    if (_fd == -1)    close (fd);    if (r == -1)    return -1;    if (lstat (basename, &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 *link){  const char *lb = NULL;  size_t len;  /* link is full path */  if (*link == '/')  {    strfcpy (newpath, link, _POSIX_PATH_MAX);    return;  }  if ((lb = strrchr (path, '/')) == NULL)  {    /* no path in link */    strfcpy (newpath, link, _POSIX_PATH_MAX);    return;  }  len = lb - path + 1;  memcpy (newpath, path, len);  strfcpy (newpath + len, link, _POSIX_PATH_MAX - len);}/* * Deference a chain of symbolic links *  * The final path is written to d. * */static intdotlock_deference_symlink (char *d, size_t l, const char *path){  struct stat sb;  char realpath[_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);      strfcpy (realpath, linkpath, sizeof (realpath));      pathptr = realpath;    }    else      break;  }  strfcpy (d, pathptr, l);  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 10static intdotlock_lock (const char *realpath){  char lockfile[_POSIX_PATH_MAX + LONG_STRING];  char nfslockfile[_POSIX_PATH_MAX + LONG_STRING];  size_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",	   realpath, Hostname, (int) getpid ());  snprintf (lockfile, sizeof (lockfile), "%s.lock", realpath);    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 intdotlock_unlock (const char *realpath){  char lockfile[_POSIX_PATH_MAX + LONG_STRING];  int i;  snprintf (lockfile, sizeof (lockfile), "%s.lock",	   realpath);    BEGIN_PRIVILEGED ();  i = unlink (lockfile);  END_PRIVILEGED ();    if (i == -1)    return DL_EX_ERROR;    return DL_EX_OK;}/* remove an empty file */static intdotlock_unlink (const char *realpath){  struct stat lsb;  int i = -1;  if (dotlock_lock (realpath) != DL_EX_OK)    return DL_EX_ERROR;  if ((i = lstat (realpath, &lsb)) == 0 && lsb.st_size == 0)    unlink (realpath);  dotlock_unlock (realpath);  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 intdotlock_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;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -