/*
* Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
- * Copyright (C) 1998 Thomas Roessler <roessler@guug.de>
+ * Copyright (C) 1998-9 Thomas Roessler <roessler@guug.de>
*
* 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
static short f_force = 0;
static short f_unlock = 0;
static short f_priv = 0;
+static short f_unlink = 0;
static int Retry = MAXLOCKATTEMPT;
#ifdef DL_STANDALONE
static int dotlock_deference_symlink(char *, size_t, const char *);
static int dotlock_prepare(char *, size_t, const char *);
+static int dotlock_check_stats (struct stat *, struct stat *);
#ifdef DL_STANDALONE
static int dotlock_init_privs(void);
* return value.
*/
-static int dotlock_try(void);
-static int dotlock_unlock(const char *);
-static int dotlock_lock(const char *);
+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
/* parse the command line options. */
- while((i = getopt(argc, argv, "tfupr:")) != EOF)
+ while((i = getopt(argc, argv, "dtfupr:")) != EOF)
{
switch(i)
{
case 'u': f_unlock = 1; break;
case 'p': f_priv = 1; break;
case 'r': Retry = atoi(optarg); break;
+ case 'd': f_unlink = 1; break;
default: usage(argv[0]);
}
}
- if(optind == argc || f_try + f_force + f_unlock > 1 || Retry < 0)
+ if(optind == argc || f_try + f_force + f_unlock + f_unlink > 1 || Retry < 0)
usage(argv[0]);
f = argv[optind];
return dotlock_try();
else if(f_unlock)
return dotlock_unlock(realpath);
+ else if (f_unlink)
+ {
+ if ((i = dotlock_lock (realpath)) != DL_EX_OK)
+ return i;
+
+ i = dotlock_unlink (realpath);
+
+ if (dotlock_unlock (realpath) != DL_EX_OK || i != DL_EX_OK)
+ return DL_EX_ERROR;
+
+ return DL_EX_OK;
+ }
else /* lock */
return dotlock_lock(realpath);
else
Retry = 0;
- f_priv = f_try = f_unlock = f_force = 0;
+ f_priv = f_try = f_unlock = f_force = f_unlink = 0;
if(flags & DL_FL_FORCE)
f_force = 1;
+ if (flags & DL_FL_UNLINK)
+ f_unlink = 1;
+
r = DL_EX_ERROR;
if(dotlock_prepare(realpath, sizeof(realpath), path) == -1)
goto bail;
usage(const char *av0)
{
fprintf(stderr, "dotlock [Mutt %s (%s)]\n", VERSION, ReleaseDate);
- fprintf(stderr, "usage: %s [-t|-f|-u] [-p] [-r <retries>] file\n",
+ 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)"
* 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, size_t l, const char *f)
{
if(lstat(basename, &lsb) == -1)
return -1;
-
- if(S_ISLNK(lsb.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 */
+
+ if (dotlock_check_stats (&fsb, &lsb) == -1)
return -1;
- }
-
+
return 0;
}
return DL_EX_OK;
}
+/* remove an empty file */
+
+static int
+dotlock_unlink (const char *realpath)
+{
+ struct stat fsb, lsb;
+ int fd = -1;
+ int i = 0;
+ char dummy;
+
+ if ((fd = open (realpath, O_RDONLY)) == -1)
+ return DL_EX_ERROR;
+
+ if ((i = fstat (fd, &fsb)) == -1)
+ goto bail;
+
+ if ((i = lstat (realpath, &lsb)) == -1)
+ goto bail;
+
+ if ((i = dotlock_check_stats (&fsb, &lsb)) == -1)
+ goto bail;
+
+ /*
+ * don't _really_ trust stat here, but actually try to read one
+ * character from the supposedly empty file.
+ */
+
+ if ((fsb.st_size == 0) && (read (fd, &dummy, 1) != 1))
+ unlink (realpath);
+
+ bail:
+
+ if (fd != -1)
+ close (fd);
+
+ return (i == 0) ? DL_EX_OK : DL_EX_ERROR;
+}
+
/*
* Check if a file can be locked at all.
mutt_quote_filename (f, sizeof (f), path);
snprintf (cmd, sizeof (cmd),
- "%s %s%s%s%s%s%s",
+ "%s %s%s%s%s%s%s%s",
NONULL (MuttDotlock),
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 0;
}
-int mx_unlock_file (const char *path, int fd)
+int mx_unlock_file (const char *path, int fd, int dot)
{
#ifdef USE_FCNTL
struct flock unlockit = { F_UNLCK, 0, 0, 0 };
#endif
#ifdef USE_DOTLOCK
- undotlock_file (path);
+ if (dot)
+ undotlock_file (path);
#endif
return 0;
return (f);
}
+void mx_unlink_empty (const char *path)
+{
+ int fd;
+#ifndef USE_DOTLOCK
+ char b;
+#endif
+
+ if ((fd = open (path, O_RDWR)) == -1)
+ return;
+
+ if (mx_lock_file (path, fd, 1, 0, 1) == -1)
+ {
+ close (fd);
+ return;
+ }
+
+#ifdef USE_DOTLOCK
+ invoke_dotlock (path, DL_FL_UNLINK, 1);
+#else
+ if (read (fd, &b, 1) != 1)
+ unlink (path);
+#endif
+
+ mx_unlock_file (path, fd, 0);
+}
+
/* try to figure out what type of mailbox ``path'' is
*
* return values:
if (ctx->msgcount == ctx->deleted &&
(ctx->magic == M_MMDF || ctx->magic == M_MBOX) &&
!mutt_is_spool(ctx->path) && !option (OPTSAVEEMPTY))
- unlink (ctx->path);
+ mx_unlink_empty (ctx->path);
mx_fastclose_mailbox (ctx);