Commit 8acde412 8acde412feee7235b342196182c69d0ed25fe1fc by Sergey Poznyakoff

Rewrite temporary file handling.

* include/mailutils/util.h (mu_make_file_name_suf): New proto.
(mu_make_file_name): Replace with a macro.
(MU_TEMPFILE_TMPDIR,MU_TEMPFILE_SUFFIX)
(MU_TEMPFILE_MKDIR): New flags.
(mu_tempfile_hints): New struct.
(mu_tempfile): Change signature.
* libmailutils/base/tempfile.c (mu_create_temp_file): New function.
(mu_tempfile): Rewrite from scratch.  Change signature.  All callers
changed.
* libmailutils/string/mkfilename.c (mu_make_file_name): Remove.
(mu_make_file_name_suf): New function.

* libmailutils/stream/streamcpy.c (mu_stream_copy): Don't return EIO
on EOF.
* libmailutils/stream/file_stream.c (mu_fd_stream_create): If
MU_STREAM_SEEK is set, position fd to 0.

* guimb/collect.c: Update calls to mu_tempfile.
* lib/mailcap.c: Likewise.
* libmailutils/base/amd.c (_amd_tempfile): Rewrite using mu_tempfile.

* libmailutils/tests/tempfile.c: New file.
* libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add tempfile.
1 parent 80bb8b2f
......@@ -55,8 +55,7 @@ collect_open_mailbox_file ()
int fd;
/* Create input mailbox */
fd = mu_tempfile (NULL, &temp_filename);
if (fd == -1)
if (mu_tempfile (NULL, 0, &fd, &temp_filename))
exit (1);
temp_file = fdopen (fd, "w");
......
......@@ -82,9 +82,27 @@ char *mu_tilde_expansion (const char *ref, const char *delim,
int mu_readlink (const char *name, char **pbuf, size_t *psize, size_t *plen);
int mu_unroll_symlink (const char *name, char **pout);
char *mu_getcwd (void);
int mu_tempfile (const char *tmpdir, char **namep);
char *mu_make_file_name_suf (const char *dir, const char *file,
const char *suf);
#define mu_make_file_name(dir, file) mu_make_file_name_suf (dir, file, NULL)
/* ------------------------ */
/* Temporary file creation. */
/* ------------------------ */
#define MU_TEMPFILE_TMPDIR 0x01 /* tmpdir is set */
#define MU_TEMPFILE_SUFFIX 0x02 /* suffix is set */
#define MU_TEMPFILE_MKDIR 0x04 /* create a directory, not a file */
struct mu_tempfile_hints
{
const char *tmpdir;
const char *suffix;
};
/*int mu_tempfile (const char *tmpdir, char **namep);*/
int mu_tempfile (struct mu_tempfile_hints *hints, int flags,
int *pfd, char **namep);
char *mu_tempname (const char *tmpdir);
char *mu_make_file_name (const char *dir, const char *file);
/* ----------------------- */
/* Current user email. */
......
......@@ -197,6 +197,7 @@ mime_context_get_content_type_value (struct mime_context *ctx,
return rc;
}
/* FIXME: Rewrite via mu_stream_copy */
static void
mime_context_write_input (struct mime_context *ctx, int fd)
{
......@@ -219,8 +220,8 @@ mime_context_get_temp_file (struct mime_context *ctx, char **ptr)
{
if (!ctx->temp_file)
{
int fd = mu_tempfile (NULL, &ctx->temp_file);
if (fd == -1)
int fd;
if (mu_tempfile (NULL, 0, &fd, &ctx->temp_file))
return -1;
mime_context_write_input (ctx, fd);
close (fd);
......
......@@ -597,13 +597,18 @@ amd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid,
return ENOSYS;
}
static FILE *
_amd_tempfile(struct _amd_data *amd, char **namep)
static int
_amd_tempfile (struct _amd_data *amd, FILE **pfile, char **namep)
{
int fd = mu_tempfile (amd->name, namep);
if (fd == -1)
return NULL;
return fdopen (fd, "w");
struct mu_tempfile_hints hints;
int fd, rc;
hints.tmpdir = amd->name;
rc = mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &fd, namep);
if (rc == 0)
if ((*pfile = fdopen (fd, "w")) == NULL)
rc = errno;
return rc;
}
static int
......@@ -658,11 +663,11 @@ _amd_message_save (struct _amd_data *amd, struct _amd_message *mhm,
return status;
}
fp = _amd_tempfile (mhm->amd, &name);
if (!fp)
status = _amd_tempfile (mhm->amd, &fp, &name);
if (status)
{
free (msg_name);
return errno;
return status;
}
/* Try to allocate large buffer */
......
......@@ -22,6 +22,8 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
......@@ -33,62 +35,177 @@
#include <mailutils/errno.h>
#include <mailutils/util.h>
/* Create and open a temporary file. Be very careful about it, since we
may be running with extra privilege i.e setgid().
Returns file descriptor of the open file.
If namep is not NULL, the pointer to the malloced file name will
be stored there. Otherwise, the file is unlinked right after open,
i.e. it will disappear after close(fd). */
#ifndef P_tmpdir
# define P_tmpdir "/tmp"
#endif
/* Lower level call.
Create a temporary file with the name according to the pattern FILENAME.
FILENAME must end with any amount of Xs, optionally followed by
SUFLEN other characters.
If ISDIR is not 0, create a directory (privileges 0700), otherwise
create a file (privileges 0600).
On success, return 0. If pfd is not NULL (and ISDIR is 0), open the
created file and store the descriptor in *PFD. Return actual file
name in FILENAME.
On error, return error code.
*/
int
mu_tempfile (const char *tmpdir, char **namep)
mu_create_temp_file (char *filename, size_t suflen, int *pfd, int isdir)
{
char *filename;
int fd;
int rc;
size_t len;
char *carrybuf;
char *p, *cp, *start, *end;
struct stat st;
static int first_call;
static char randstate[256];
static const unsigned char alphabet[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
if (!first_call)
{
/* Initialize random number generator */
struct timeval tv;
gettimeofday (&tv, NULL);
initstate (((unsigned long) tv.tv_usec << 16) ^ tv.tv_sec,
randstate, sizeof (randstate));
first_call = 1;
}
setstate (randstate);
if (!tmpdir)
tmpdir = (getenv ("TMPDIR")) ? getenv ("TMPDIR") : P_tmpdir;
filename = mu_make_file_name (tmpdir, "muXXXXXX");
if (!filename)
/* Start with the last filename character before suffix */
end = filename + strlen (filename) - suflen - 1;
/* Fill X's with random characters */
for (p = end; p >= filename && *p == 'X'; p--)
*p = alphabet[random () % (sizeof (alphabet) - 1)];
len = end - p;
if (len == 0)
return EINVAL;
start = p + 1;
carrybuf = malloc (len);
if (!carrybuf)
return ENOMEM;
/* Fill in the carry buffer */
memcpy (carrybuf, start, len);
for (;;)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_make_file_name", NULL, ENOMEM);
return -1;
if (isdir)
{
if (mkdir (filename, 0700) == 0)
{
rc = 0;
break;
}
}
else if (pfd)
{
if ((*pfd = open (filename, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
{
rc = 0;
break;
}
}
else if (lstat (filename, &st) && errno == ENOENT)
{
rc = 0;
break;
}
if (errno != EEXIST)
{
rc = errno;
break;
}
for (p = start, cp = carrybuf;; p++, cp++)
{
char *q;
if (p == end)
/* All permutation exhausted */
return EEXIST;
q = strchr ((char*)alphabet, *p);
if (!q)
abort (); /* should not happen */
*p = (q[1] == 0) ? alphabet[0] : q[1];
if (*p != *cp)
break;
}
}
free (carrybuf);
return rc;
}
#ifdef HAVE_MKSTEMP
{
int save_mask = umask (077);
fd = mkstemp (filename);
umask (save_mask);
}
#else
if (mktemp (filename))
fd = open (filename, O_CREAT|O_EXCL|O_RDWR, 0600);
else
fd = -1;
#endif
/* Create a temporary file according to HINTS.
If MU_TEMPFILE_MKDIR flag is set, create a directory instead of file.
On success, return 0. If PFD is not NULL (and MU_TEMPFILE_MKDIR flag
is not set), open the file and store its descriptor in *PFD. If
NAMEP is not NULL, store there a pointer to the allocated file name.
At least one of PFD and NAMEP must be set, otherwise EINVAL is returned.
if (fd == -1)
On error, returns error code describing the problem.
If hints is NULL, any flags except MU_TEMPFILE_MKDIR are ignored.
Otherwise:
* If MU_TEMPFILE_TMPDIR is set, hints->tmpdir points to the name of
a directory where to create the temporary.
* If MU_TEMPFILE_SUFFIX is set, hints->suffix defines a suffix to
append to the created file name.
*/
int
mu_tempfile (struct mu_tempfile_hints *hints, int flags,
int *pfd, char **namep)
{
char *filename;
const char *tmpdir = getenv ("TMPDIR");
const char *suf = NULL;
int create_dir = 0;
int rc;
struct stat st;
if (pfd == NULL && namep == NULL)
return EINVAL;
if (hints)
{
mu_error (_("cannot open temporary file: %s"), mu_strerror (errno));
free (filename);
return -1;
if (flags & MU_TEMPFILE_TMPDIR)
tmpdir = hints->tmpdir;
if (flags & MU_TEMPFILE_SUFFIX)
suf = hints->suffix;
create_dir = flags & MU_TEMPFILE_MKDIR;
}
if (!tmpdir)
tmpdir = P_tmpdir;
/* First, see if tmpdir exists */
if (stat (tmpdir, &st))
return errno;
if (namep)
*namep = filename;
else
/* Create a name template */
filename = mu_make_file_name_suf (tmpdir, "muXXXXXX", suf);
rc = mu_create_temp_file (filename, suf ? strlen (suf) : 0, pfd, create_dir);
if (rc == 0)
{
unlink (filename);
free (filename);
if (namep)
*namep = filename;
else
{
unlink (filename);
free (filename);
}
}
return fd;
return rc;
}
/* Create a unique temporary file name in tmpdir. The function
......@@ -100,10 +217,13 @@ mu_tempfile (const char *tmpdir, char **namep)
char *
mu_tempname (const char *tmpdir)
{
struct mu_tempfile_hints hints;
char *filename = NULL;
int fd = mu_tempfile (tmpdir, &filename);
int fd;
hints.tmpdir = tmpdir;
if (mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &fd, &filename))
return NULL;
close (fd);
return filename;
}
......
......@@ -388,9 +388,17 @@ int
mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd, int flags)
{
struct _mu_file_stream *fstr;
int rc = _mu_file_stream_create (&fstr,
sizeof (struct _mu_file_stream),
filename, fd, flags|_MU_STR_OPEN);
int rc;
if (flags & MU_STREAM_SEEK)
{
if (lseek (fd, 0, SEEK_SET))
return errno;
}
rc = _mu_file_stream_create (&fstr,
sizeof (struct _mu_file_stream),
filename, fd, flags|_MU_STR_OPEN);
if (rc == 0)
{
mu_stream_t stream = (mu_stream_t) fstr;
......
......@@ -98,10 +98,7 @@ mu_stream_copy (mu_stream_t dst, mu_stream_t src, mu_off_t size,
if (status)
break;
if (n == 0)
{
status = EIO;
break;
}
break;
status = mu_stream_write (dst, buf, n, NULL);
if (status)
break;
......@@ -120,6 +117,10 @@ mu_stream_copy (mu_stream_t dst, mu_stream_t src, mu_off_t size,
if (pcsz)
*pcsz = total;
/* FIXME: When EOF error code is implemented:
else if (total == 0)
status = EIO;
*/
free (buf);
return status;
}
......
......@@ -37,11 +37,10 @@ static int
fd_temp_open (struct _mu_stream *str)
{
struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
int fd = mu_tempfile (fstr->filename, NULL);
if (fd == -1)
return errno;
fstr->fd = fd;
return 0;
struct mu_tempfile_hints hints;
hints.tmpdir = fstr->filename;
return mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &fstr->fd, NULL);
}
int
......
......@@ -25,22 +25,27 @@
#include <mailutils/util.h>
char *
mu_make_file_name (const char *dir, const char *file)
mu_make_file_name_suf (const char *dir, const char *file, const char *suf)
{
char *tmp;
size_t dirlen = strlen (dir);
size_t suflen = suf ? strlen (suf) : 0;
size_t fillen = strlen (file);
size_t len;
while (dirlen > 0 && dir[dirlen-1] == '/')
dirlen--;
len = dirlen + 1 + strlen (file);
len = dirlen + 1 + fillen + suflen;
tmp = mu_alloc (len + 1);
if (tmp)
{
memcpy (tmp, dir, dirlen);
tmp[dirlen++] = '/';
strcpy (tmp + dirlen, file);
memcpy (tmp + dirlen, file, fillen);
if (suf)
memcpy (tmp + dirlen + fillen, suf, suflen);
tmp[len] = 0;
}
return tmp;
}
......
......@@ -48,6 +48,7 @@ noinst_PROGRAMS = \
listop\
mailcap\
prop\
tempfile\
url-parse\
wicket\
wsp
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
GNU Mailutils 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, or (at your option)
any later version.
GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mailutils/util.h>
#include <mailutils/errno.h>
#include <mailutils/error.h>
#include <mailutils/stream.h>
char *progname;
void
usage (FILE *fp, int code)
{
fprintf (fp,
"usage: %s [-tmpdir=DIR] [-suffix=SUF] [-dry-run | -unlink] { file | dir }\n",
progname);
exit (code);
}
int
main (int argc, char **argv)
{
struct mu_tempfile_hints hints, *phints;
int flags = 0;
int fd;
int *pfd = &fd;
char *filename;
char **pname = &filename;
char *infile = NULL;
int verify = 0;
int verbose = 0;
progname = argv[0];
if (argc == 1)
usage (stdout, 0);
while (--argc)
{
char *arg = *++argv;
if (strncmp (arg, "-tmpdir=", 8) == 0)
{
hints.tmpdir = arg + 8;
flags |= MU_TEMPFILE_TMPDIR;
}
else if (strncmp (arg, "-suffix=", 8) == 0)
{
hints.suffix = arg + 8;
flags |= MU_TEMPFILE_SUFFIX;
}
else if (strcmp (arg, "-dry-run") == 0)
pfd = NULL;
else if (strcmp (arg, "-unlink") == 0)
pname = NULL;
else if (strncmp (arg, "-infile=", 8) == 0)
infile = arg + 8;
else if (strncmp (arg, "-verify", 7) == 0)
verify = 1;
else if (strncmp (arg, "-verbose", 8) == 0)
verbose = 1;
else
break;
}
if (argv[0] == NULL)
usage (stderr, 1);
if (strcmp (argv[0], "file") == 0)
/* nothing */;
else if (strcmp (argv[0], "dir") == 0)
flags |= MU_TEMPFILE_MKDIR;
else
usage (stderr, 1);
if (pname == NULL && pfd == NULL)
{
mu_error ("both -unlink and -dry-run given");
exit (1);
}
if (infile)
{
if (flags & MU_TEMPFILE_MKDIR)
{
mu_error ("-infile is useless with dir");
exit (1);
}
else if (pfd == NULL)
{
mu_error ("-infile is useless with -dry-run");
exit (1);
}
}
if (verify && pfd == NULL)
{
mu_error ("-verify is useless with -dry-run");
exit (1);
}
phints = flags ? &hints : NULL;
MU_ASSERT (mu_tempfile (phints, flags, pfd, pname));
if (filename)
printf ("created file name %s\n", filename);
if (!pfd)
return 0;
if (infile)
{
mu_stream_t in, out;
mu_off_t size;
if (strcmp (infile, "-") == 0)
MU_ASSERT (mu_stdio_stream_create (&in, MU_STDIN_FD, 0));
else
MU_ASSERT (mu_file_stream_create (&in, infile, MU_STREAM_READ));
MU_ASSERT (mu_fd_stream_create (&out, filename, fd, MU_STREAM_WRITE));
MU_ASSERT (mu_stream_copy (out, in, 0, &size));
if (verbose)
printf ("copied %lu bytes to the temporary\n", (unsigned long) size);
mu_stream_unref (out);
mu_stream_unref (in);
}
if (verify)
{
mu_stream_t in, out;
mu_off_t size;
MU_ASSERT (mu_stdio_stream_create (&out, MU_STDOUT_FD, 0));
MU_ASSERT (mu_fd_stream_create (&in, filename, fd,
MU_STREAM_READ|MU_STREAM_SEEK));
MU_ASSERT (mu_stream_copy (out, in, 0, &size));
if (verbose)
printf ("dumped %lu bytes\n", (unsigned long) size);
mu_stream_unref (out);
mu_stream_unref (in);
}
close (fd);
return 0;
}
......@@ -324,11 +324,19 @@ escape_run_editor (char *ed, int argc, char **argv, compose_env_t *env)
if (!mailvar_get (NULL, "editheaders", mailvar_type_boolean, 0))
{
char *filename;
int fd = mu_tempfile (NULL, &filename);
FILE *fp = fdopen (fd, "w+");
int fd;
FILE *fp;
char buffer[512];
int rc;
rc = mu_tempfile (NULL, 0, &fd, &filename);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_tempfile", NULL, rc);
return rc;
}
fp = fdopen (fd, "w+");
dump_headers (fp, env);
rewind (env->file);
......@@ -639,8 +647,7 @@ escape_pipe (int argc, char **argv, compose_env_t *env)
return 1;
}
fd = mu_tempfile (NULL, NULL);
if (fd == -1)
if (mu_tempfile (NULL, 0, &fd, NULL))
return 1;
if ((pid = fork ()) < 0)
......
......@@ -305,8 +305,18 @@ fill_body (mu_message_t msg, mu_stream_t instr)
mu_stream_t stream = NULL;
mu_off_t n;
mu_message_get_body (msg, &body);
mu_body_get_streamref (body, &stream);
rc = mu_message_get_body (msg, &body);
if (rc)
{
mu_error (_("cannot get message body: %s"), mu_strerror (rc));
return 1;
}
rc = mu_body_get_streamref (body, &stream);
if (rc)
{
mu_error (_("cannot get body: %s"), mu_strerror (rc));
return 1;
}
rc = mu_stream_copy (stream, instr, 0, &n);
mu_stream_destroy (&stream);
......@@ -428,16 +438,16 @@ int
mail_send0 (compose_env_t * env, int save_to)
{
int done = 0;
int fd;
int fd, rc;
char *filename;
char *savefile = NULL;
int int_cnt;
char *escape;
fd = mu_tempfile (NULL, &filename);
if (fd == -1)
rc = mu_tempfile (NULL, 0, &fd, &filename);
if (rc)
{
util_error (_("Cannot open temporary file"));
util_error (_("Cannot open temporary file: %s"), mu_strerror (rc));
return 1;
}
......