summaryrefslogtreecommitdiff
path: root/libmailutils/file_stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/file_stream.c')
-rw-r--r--libmailutils/file_stream.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/libmailutils/file_stream.c b/libmailutils/file_stream.c
new file mode 100644
index 000000000..0df1254e1
--- /dev/null
+++ b/libmailutils/file_stream.c
@@ -0,0 +1,312 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2004,
+ 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <mailutils/types.h>
+#include <mailutils/alloc.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+#include <mailutils/stream.h>
+#include <mailutils/sys/stream.h>
+#include <mailutils/sys/file_stream.h>
+#include <mailutils/mutil.h>
+
+#define MU_FSTR_ERROR_NOREG (MU_ERR_LAST+1)
+
+static int
+fd_read (struct _mu_stream *str, char *buf, size_t size, size_t *pret)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ ssize_t n = read (fstr->fd, buf, size);
+ if (n == -1)
+ return errno;
+ *pret = n;
+ return 0;
+}
+
+static int
+fd_write (struct _mu_stream *str, const char *buf, size_t size, size_t *pret)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ ssize_t n = write (fstr->fd, (char*) buf, size);
+ if (n == -1)
+ return errno;
+ *pret = n;
+ return 0;
+}
+
+static int
+fd_close (struct _mu_stream *str)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ if (fstr->fd != -1)
+ {
+ if ((str->flags & MU_STREAM_AUTOCLOSE) && close (fstr->fd))
+ return errno;
+ fstr->fd = -1;
+ }
+ return 0;
+}
+
+static int
+fd_open (struct _mu_stream *str)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ int oflg;
+ int fd;
+
+ if (!fstr->filename)
+ return EINVAL;
+ if (fstr->fd != -1)
+ fd_close (str);
+
+ /* Map the flags to the system equivalent. */
+ if ((fstr->stream.flags & MU_STREAM_RDWR) == MU_STREAM_RDWR)
+ oflg = O_RDWR;
+ else if (fstr->stream.flags & (MU_STREAM_WRITE|MU_STREAM_APPEND))
+ oflg = O_WRONLY;
+ else /* default */
+ oflg = O_RDONLY;
+
+ if (fstr->stream.flags & MU_STREAM_APPEND)
+ oflg |= O_APPEND;
+
+ /* Handle CREAT with care, not to follow symlinks. */
+ if (fstr->stream.flags & MU_STREAM_CREAT)
+ {
+ /* First see if the file already exists. */
+ fd = open (fstr->filename, oflg);
+ if (fd == -1)
+ {
+ /* Oops bail out. */
+ if (errno != ENOENT)
+ return errno;
+ /* Race condition here when creating the file ??. */
+ fd = open (fstr->filename, oflg|O_CREAT|O_EXCL,
+ 0600 | mu_stream_flags_to_mode (fstr->stream.flags, 0));
+ }
+ }
+ else
+ fd = open (fstr->filename, oflg);
+
+ if (fd == -1)
+ return errno;
+
+ if (!(fstr->stream.flags & MU_STREAM_ALLOW_LINKS)
+ && (fstr->stream.flags & (MU_STREAM_CREAT | MU_STREAM_RDWR |
+ MU_STREAM_APPEND)))
+ {
+ struct stat fdbuf, filebuf;
+
+ /* The next two stats should never fail. */
+ if (fstat (fd, &fdbuf) == -1
+ || lstat (fstr->filename, &filebuf) == -1)
+ {
+ close (fd);
+ return errno;
+ }
+
+ /* Now check that: file and fd reference the same file,
+ file only has one link, file is plain file. */
+ if ((fdbuf.st_dev != filebuf.st_dev
+ || fdbuf.st_ino != filebuf.st_ino
+ || fdbuf.st_nlink != 1
+ || filebuf.st_nlink != 1
+ || (fdbuf.st_mode & S_IFMT) != S_IFREG))
+ {
+ /* FIXME: Be silent */
+ close (fd);
+ return MU_FSTR_ERROR_NOREG;
+ }
+ }
+
+ if (fd < 0)
+ return errno;
+
+ /* Make sure it will be closed */
+ fstr->flags |= MU_STREAM_AUTOCLOSE;
+
+ fstr->fd = fd;
+ return 0;
+}
+
+int
+fd_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *presult)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ off = lseek (fstr->fd, off, SEEK_SET);
+ if (off < 0)
+ return errno;
+ *presult = off;
+ return 0;
+}
+
+int
+fd_size (struct _mu_stream *str, mu_off_t *psize)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ struct stat st;
+ if (fstat (fstr->fd, &st))
+ return errno;
+ *psize = st.st_size;
+ return 0;
+}
+
+void
+fd_done (struct _mu_stream *str)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ if (fstr->fd != -1)
+ fd_close (str);
+ if (fstr->filename)
+ free (fstr->filename);
+}
+
+const char *
+fd_error_string (struct _mu_stream *str, int rc)
+{
+ if (rc == MU_FSTR_ERROR_NOREG)
+ return _("must be a plain file with one link");
+ else
+ return mu_strerror (rc);
+}
+
+static int
+fd_ioctl (struct _mu_stream *str, int code, void *ptr)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ mu_transport_t *ptrans;
+
+ switch (code)
+ {
+ case MU_IOCTL_GET_TRANSPORT:
+ if (!ptr)
+ return EINVAL;
+ ptrans = ptr;
+ ptrans[0] = (mu_transport_t) fstr->fd;
+ ptrans[1] = NULL;
+ break;
+
+ case MU_IOCTL_SET_TRANSPORT:
+ if (!ptr)
+ return EINVAL;
+ ptrans = ptr;
+ fstr->fd = (int) ptrans[0];
+ break;
+
+ case MU_IOCTL_GET_TRANSPORT_BUFFER:
+ {
+ struct mu_buffer_query *qp = ptr;
+ return mu_stream_get_buffer (str, qp);
+ }
+
+ case MU_IOCTL_SET_TRANSPORT_BUFFER:
+ {
+ struct mu_buffer_query *qp = ptr;
+ return mu_stream_set_buffer (str, qp->buftype, qp->bufsize);
+ }
+
+ default:
+ return ENOSYS;
+ }
+ return 0;
+}
+
+int
+fd_wait (mu_stream_t stream, int *pflags, struct timeval *tvp)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream;
+
+ if (fstr->fd == -1)
+ return EINVAL;
+ return mu_fd_wait (fstr->fd, pflags, tvp);
+}
+
+int
+fd_truncate (mu_stream_t stream, mu_off_t size)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream;
+ if (ftruncate (fstr->fd, size))
+ return errno;
+ return 0;
+}
+
+int
+_mu_file_stream_create (struct _mu_file_stream **pstream, size_t size,
+ const char *filename, int fd, int flags)
+{
+ struct _mu_file_stream *str =
+ (struct _mu_file_stream *) _mu_stream_create (size, flags);
+ if (!str)
+ return ENOMEM;
+
+ str->stream.read = fd_read;
+ str->stream.write = fd_write;
+ str->stream.open = fd_open;
+ str->stream.close = fd_close;
+ str->stream.done = fd_done;
+ str->stream.seek = fd_seek;
+ str->stream.size = fd_size;
+ str->stream.ctl = fd_ioctl;
+ str->stream.wait = fd_wait;
+ str->stream.truncate = fd_truncate;
+ str->stream.error_string = fd_error_string;
+
+ if (filename)
+ str->filename = mu_strdup (filename);
+ else
+ str->filename = NULL;
+ str->fd = fd;
+ str->flags = 0;
+ *pstream = str;
+ return 0;
+}
+
+int
+mu_file_stream_create (mu_stream_t *pstream, const char *filename, int flags)
+{
+ struct _mu_file_stream *fstr;
+ int rc = _mu_file_stream_create (&fstr,
+ sizeof (struct _mu_file_stream),
+ filename, -1,
+ flags | MU_STREAM_SEEK | MU_STREAM_AUTOCLOSE);
+ if (rc == 0)
+ *pstream = (mu_stream_t) fstr;
+ return rc;
+}
+
+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);
+ if (rc == 0)
+ *pstream = (mu_stream_t) fstr;
+ return rc;
+}
+

Return to:

Send suggestions and report system problems to the System administrator.