On Wed, Oct 06, 2010 at 02:31:45PM +0200, Matteo Cypriani wrote:
> > So... can you build this one - without hardening would be enough?
> 
> # ./dma-migrate -v
> RDBG 001 before srandom()
> RDBG 002 before getopt()
> RDBG 002v
> Internal problem: unexpected getopt() return value: 255
[snip]
> 
> Sounds like a progress…

Argh!

Okay, I see the problem now.  It's mostly a case of me being a bit
stupid at times, though this particular mistake is one I hadn't made
in almost ten years - and as luck would have it, GCC does indeed treat
char as unsigned on PowerPC, so the getopt() loop never finishes and
dma-migrate never reaches the point where it will actually migrate stuff.

Mmmmkay, here's another version of the tool for you to test, although
I'm pretty sure it will just work now.  If it works, I'll upload
a fixed package tonight or tomorrow, and I'll see if the release team
will approve a freeze exception.

So... try this? (basically a "char ch" to "int ch" fix)  I'm 99.5% sure
that this *is* the problem, so I'll prepare a package for upload.

G'luck,
Peter

-- 
Peter Pentchev  r...@space.bg    r...@ringlet.net    r...@freebsd.org
PGP key:        http://people.FreeBSD.org/~roam/roam.key.asc
Key fingerprint FDBA FD79 C26F 3C51 C95E  DF9E ED18 B68D 1619 4553
If I had finished this sentence,
/*-
 * Copyright (c) 2010  Peter Pentchev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>

#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#ifndef __printflike
#ifdef __GNUC__
#define __printflike(fmtarg, firstvararg)	\
	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#else
#define __printflike(fmtarg, firstvararg)
#endif
#endif

#define DEFAULT_SPOOLDIR	"/var/spool/dma"

static int	 verbose = 0;
static char	 copybuf[BUFSIZ];

static int	 dma_migrate(int, const char *);

static int	 open_locked(const char *, int, ...);
static void	 cleanup_file(int, char *);

static void	 usage(int);
static void	 version(void);
static void	 debug(const char *, ...) __printflike(1, 2);

int
main(int argc, char **argv)
{
	const char *spooldir;
	int hflag, Vflag, errs, fd, res;
	int ch;
	DIR *d;
	struct dirent *e;
	struct stat sb;

	fprintf(stderr, "RDBG 001 before srandom()\n");
        srandom((unsigned long)((time(NULL) ^ getpid()) + ((uintptr_t)argv)));

	hflag = Vflag = 0;
	spooldir = DEFAULT_SPOOLDIR;
	fprintf(stderr, "RDBG 002 before getopt()\n");
	while (ch = getopt(argc, argv, "d:hVv"), ch != -1)
		switch (ch) {
			case 'd':
				fprintf(stderr, "RDBG 002d '%s'\n", optarg);
				spooldir = optarg;
				break;

			case 'h':
				fprintf(stderr, "RDBG 002h\n");
				hflag = 1;
				break;

			case 'V':
				fprintf(stderr, "RDBG 002V\n");
				Vflag = 1;
				break;

			case 'v':
				fprintf(stderr, "RDBG 002v\n");
				verbose = 1;
				break;

			case '?':
				fprintf(stderr, "RDBG 002?\n");
				usage(1);
				/* NOTREACHED */

			default:
				fprintf(stderr, "Internal problem: unexpected getopt() return value: %d\n", ch);
				return (1);
		}
	fprintf(stderr, "RDBG 002.1 after the getopt loop\n");
	if (Vflag)
		version();
	if (hflag)
		usage(0);
	if (hflag || Vflag)
		exit(0);

	fprintf(stderr, "RDBG 003 after getopt()\n");
	argc -= optind;
	argv += optind;
	fprintf(stderr, "RDBG 004 argc = %d argv = %p\n", argc, argv);

	/* Let's roll! */
	if (chdir(spooldir) == -1)
		err(1, "Could not change into spool directory %s", spooldir);
	fprintf(stderr, "RDBG 005 after chdir()\n");
	if (d = opendir("."), d == NULL)
		err(1, "Could not read spool directory %s", spooldir);
	fprintf(stderr, "RDBG 005 after opendir()\n");
	errs = 0;
	while (e = readdir(d), e != NULL) {
		/* Do we care about this entry? */
		fprintf(stderr, "RDBG 006 read a dir entry %s\n", e->d_name);
		debug("Read a directory entry: %s\n", e->d_name);
		if (strncmp(e->d_name, "tmp_", 4) == 0 ||
		    e->d_name[0] == 'M' || e->d_name[0] == 'Q' ||
		    (e->d_type != DT_REG && e->d_type != DT_UNKNOWN)) {
			fprintf(stderr, "RDBG 007 skipping it\n");
			continue;
		}
		if (e->d_type == DT_UNKNOWN) {
			fprintf(stderr, "RDBG 008 additional stat()\n");
			if (stat(e->d_name, &sb) == -1 || !S_ISREG(sb.st_mode)) {
				fprintf(stderr, "RDBG 009 skipping it\n");
				continue;
			}
			fprintf(stderr, "RDBG 010 not skipping a DT_UNKNOWN entry\n");
		}
		fprintf(stderr, "RDBG 011 want to process it\n");
		debug("- want to process it\n");

		/* Try to lock it - skip it if dma is delivering the message */
		if (fd = open_locked(e->d_name, O_RDONLY|O_NDELAY), fd == -1) {
			fprintf(stderr, "RDBG 012 open_locked() failed\n");
			debug("- seems to be locked, skipping\n");
			continue;
		}

		/* Okay, convert it to the M/Q schema */
		fprintf(stderr, "RDBG 013 before dma_migrate()\n");
		res = dma_migrate(fd, e->d_name);
		fprintf(stderr, "RDBG 014 after dma_migrate(), res = %d\n", res);
		close(fd);
		if (res == -1)
			errs++;
		fprintf(stderr, "RDBG 015 on with the loop\n");
	}
	fprintf(stderr, "RDBG 016 finished, errs = %d\n", errs);
	if (errs)
		debug("Finished, %d conversion errors\n", errs);
	else
		debug("Everything seems to be all right\n");
	fprintf(stderr, "RDBG 017 returning %d\n", errs && 1);
	return (errs && 1);
}

static int
dma_migrate(int fd, const char *fname)
{
	const char *id;
	char *mname, *qname, *tempname, *sender, *recp, *line, *recpline;
	int mfd, qfd, tempfd;
	struct stat sb;
	FILE *fp, *qfp, *mfp;
	size_t sz, len;
	static regex_t *qidreg = NULL;

	mfd = tempfd = qfd = -1;
	mname = qname = sender = recp = line = NULL;
	fp = qfp = NULL;

	if (fstat(fd, &sb) == -1) {
		warn("Could not fstat(%s)", fname);
		return (-1);
	}
	/*
	 * Let's just blithely assume that the queue ID *is* the filename,
	 * since that's the way dma did things so far.
	 * Well, okay, let's check it.
	 */
	if (qidreg == NULL) {
		regex_t *nreg;

		if ((nreg = malloc(sizeof(*qidreg))) == NULL) {
			warn("Could not allocate memory for a regex");
			return (-1);
		}
		if (regcomp(nreg, "^[a-fA-F0-9]\\+\\.[a-fA-F0-9]\\+$", 0)
		    != 0) {
			warnx("Could not compile a dma queue ID regex");
			free(nreg);
			return (-1);
		}
		qidreg = nreg;
	}
	if (regexec(qidreg, fname, 0, NULL, 0) != 0) {
		warnx("The name '%s' is not a valid dma queue ID", fname);
		return (-1);
	}
	id = fname;
	debug("  - queue ID %s\n", id);
	if (asprintf(&mname, "M%s", id) == -1 ||
	    asprintf(&tempname, "tmp_%s", id) == -1 ||
	    asprintf(&qname, "Q%s", id) == -1 ||
	    mname == NULL || tempname == NULL || qname == NULL)
		goto fail;

	/* Create the message placeholder early to avoid races */
	mfd = open_locked(mname, O_CREAT | O_EXCL | O_RDWR, 0600);
	if (mfd == -1) {
		warn("Could not create temporary file %s", mname);
		goto fail;
	}
	if (stat(qname, &sb) != -1 || errno != ENOENT ||
	    stat(tempname, &sb) != -1 || errno != ENOENT) {
		warnx("Some of the queue files for %s already exist", fname);
		goto fail;
	}
	debug("  - mfd %d names %s, %s, %s\n", mfd, mname, tempname, qname);

	fp = fdopen(fd, "r");
	if (fp == NULL) {
		warn("Could not reopen the descriptor for %s", fname);
		goto fail;
	}

	/* Parse the header of the old-format message file */
	/* ...sender... */
	if (getline(&sender, &sz, fp) == -1) {
		warn("Could not read the initial line from %s", fname);
		goto fail;
	}
	sz = strlen(sender);
	while (sz > 0 && (sender[sz - 1] == '\n' || sender[sz - 1] == '\r'))
		sender[--sz] = '\0';
	if (sz == 0) {
		warnx("Empty sender line in %s", fname);
		goto fail;
	}
	debug("  - sender %s\n", sender);
	/* ...recipient(s)... */
	len = strlen(fname);
	recpline = NULL;
	while (1) {
		if (getline(&line, &sz, fp) == -1) {
			warn("Could not read a recipient line from %s", fname);
			goto fail;
		}
		sz = strlen(line);
		while (sz > 0 &&
		    (line[sz - 1] == '\n' || line[sz - 1] == '\r'))
			line[--sz] = '\0';
		if (sz == 0) {
			free(line);
			line = NULL;
			break;
		}
		if (recp == NULL &&
		    strncmp(line, fname, len) == 0 && line[len] == ' ') {
			recp = line + len + 1;
			recpline = line;
		} else {
			free(line);
		}
		line = NULL;
	}
	if (recp == NULL) {
		warnx("Could not find its own recipient line in %s", fname);
		goto fail;
	}
	/* ..phew, finished with the header. */

	tempfd = open_locked(tempname, O_CREAT | O_EXCL | O_RDWR, 0600);
	if (tempfd == -1) {
		warn("Could not create a queue file for %s", fname);
		goto fail;
	}
	qfp = fdopen(tempfd, "w");
	if (qfp == NULL) {
		warn("Could not fdopen(%s) for %s", tempname, fname);
		goto fail;
	}
	mfp = fdopen(mfd, "w");
	if (mfp == NULL) {
		warn("Could not fdopen(%s) for %s", mname, fname);
		goto fail;
	}
	fprintf(qfp, "ID: %s\nSender: %s\nRecipient: %s\n", id, sender, recp);
	fflush(qfp);
	fsync(tempfd);

	/* Copy the message file over to mname */
	while ((sz = fread(copybuf, 1, sizeof(copybuf), fp)) > 0)
		if (fwrite(copybuf, 1, sz, mfp) != sz) {
			warn("Could not copy the message from %s to %s",
			    fname, mname);
			goto fail;
		}
	if (ferror(fp)) {
		warn("Could not read the full message from %s", fname);
		goto fail;
	}
	fflush(mfp);
	fsync(mfd);

	if (rename(tempname, qname) == -1) {
		warn("Could not rename the queue file for %s", fname);
		goto fail;
	}
	qfd = tempfd;
	tempfd = -1;
	if (unlink(fname) == -1) {
		warn("Could not remove the old converted file %s", fname);
		goto fail;
	}

	fclose(fp);
	fclose(qfp);
	free(sender);
	free(line);
	free(recpline);
	free(mname);
	free(qname);
	free(tempname);
	return (0);

fail:
	if (fp != NULL)
		fclose(fp);
	if (qfp != NULL)
		fclose(qfp);
	if (sender != NULL)
		free(sender);
	if (line != NULL)
		free(line);
	if (recpline != NULL)
		free(recpline);
	cleanup_file(mfd, mname);
	cleanup_file(qfd, qname);
	cleanup_file(tempfd, tempname);
	return (-1);
}

static void
cleanup_file(int fd, char *fname)
{
	if (fd != -1) {
		close(fd);
		unlink(fname);
	}
	if (fname != NULL)
		free(fname);
}

static void
usage(int ferr)
{
	const char *s =
	    "Usage:\tdma-migrate [-hVv] [-d spooldir]\n"
	    "\t-d\tspecify the spool directory (" DEFAULT_SPOOLDIR ")\n"
	    "\t-h\tdisplay program usage information and exit\n"
	    "\t-V\tdisplay program version information and exit\n"
	    "\t-v\tverbose operation - display diagnostic messages";

	if (ferr)
		errx(1, "%s", s);
	puts(s);
}

static void
version(void)
{
	printf("dma-migrate 0.01 (dma 0.0.2010.06.17)\n");
}

static void
debug(const char *fmt, ...)
{
	va_list v;

	if (verbose < 1)
		return;
	va_start(v, fmt);
	vfprintf(stderr, fmt, v);
	va_end(v);
}

static int
open_locked(const char *fname, int flags, ...)
{
	int mode = 0;
#ifndef O_EXLOCK
	int fd, save_errno;
#endif

	if (flags & O_CREAT) {
		va_list ap;
		va_start(ap, flags);
		mode = va_arg(ap, int);
		va_end(ap);
	}

#ifndef O_EXLOCK
	fd = open(fname, flags, mode);
	if (fd < 0)
		return(fd);
	if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
		save_errno = errno;
		close(fd);
		errno = save_errno;
		return(-1);
	}
	return(fd);
#else
	return(open(fname, flags|O_EXLOCK, mode));
#endif
}

Attachment: signature.asc
Description: Digital signature

Reply via email to