trail.c revision 285830
199461Sobrien/*-
299461Sobrien * Copyright (c) 2012 The FreeBSD Foundation
399461Sobrien * All rights reserved.
499461Sobrien *
599461Sobrien * This software was developed by Pawel Jakub Dawidek under sponsorship from
699461Sobrien * the FreeBSD Foundation.
799461Sobrien *
892828Sobrien * Redistribution and use in source and binary forms, with or without
992828Sobrien * modification, are permitted provided that the following conditions
1092828Sobrien * are met:
1192828Sobrien * 1. Redistributions of source code must retain the above copyright
1291041Sobrien *    notice, this list of conditions and the following disclaimer.
1391041Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1491041Sobrien *    notice, this list of conditions and the following disclaimer in the
1591041Sobrien *    documentation and/or other materials provided with the distribution.
1691041Sobrien *
1791041Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1891041Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1991041Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2091041Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2191041Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2291041Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2391041Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2491041Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2591041Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2691041Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2791041Sobrien * SUCH DAMAGE.
2891041Sobrien *
2991041Sobrien * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/trail.c#3 $
3091041Sobrien */
3189857Sobrien
3278828Sobrien#include <config/config.h>
3389857Sobrien
3489857Sobrien#include <sys/param.h>
3578828Sobrien#include <sys/stat.h>
3689857Sobrien
3778828Sobrien#include <dirent.h>
3889857Sobrien#include <errno.h>
3978828Sobrien#include <fcntl.h>
4089857Sobrien#include <stdbool.h>
4178828Sobrien#include <stdint.h>
4289857Sobrien#include <stdlib.h>
4389857Sobrien#include <string.h>
4478828Sobrien#include <unistd.h>
4589857Sobrien
4678828Sobrien#include <compat/compat.h>
4789857Sobrien#ifndef HAVE_STRLCPY
4889857Sobrien#include <compat/strlcpy.h>
4989857Sobrien#endif
5089857Sobrien#ifndef HAVE_FACCESSAT
5189857Sobrien#include "faccessat.h"
5289857Sobrien#endif
5389857Sobrien#ifndef HAVE_FSTATAT
5489857Sobrien#include "fstatat.h"
5589857Sobrien#endif
5678828Sobrien#ifndef HAVE_OPENAT
5789857Sobrien#include "openat.h"
5889857Sobrien#endif
5989857Sobrien#ifndef HAVE_UNLINKAT
6089857Sobrien#include "unlinkat.h"
6189857Sobrien#endif
6289857Sobrien
6389857Sobrien#include "pjdlog.h"
6489857Sobrien#include "trail.h"
6589857Sobrien
6689857Sobrien#define	TRAIL_MAGIC	0x79a11
6789857Sobrienstruct trail {
6889857Sobrien	int	 tr_magic;
6989857Sobrien	/* Path usually to /var/audit/dist/ directory. */
7089857Sobrien	char	 tr_dirname[PATH_MAX];
7189857Sobrien	/* Descriptor to td_dirname directory. */
7289857Sobrien	DIR	*tr_dirfp;
7389857Sobrien	/* Path to audit trail file. */
7489857Sobrien	char	 tr_filename[PATH_MAX];
7589857Sobrien	/* Descriptor to audit trail file. */
7689857Sobrien	int	 tr_filefd;
7789857Sobrien};
7889857Sobrien
7989857Sobrien#define	HALF_LEN	14
8089857Sobrien
8189857Sobrienbool
8289857Sobrientrail_is_not_terminated(const char *filename)
8389857Sobrien{
8489857Sobrien
8589857Sobrien	return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
8689857Sobrien}
8789857Sobrien
8889857Sobrienbool
8989857Sobrientrail_is_crash_recovery(const char *filename)
9089857Sobrien{
9189857Sobrien
9289857Sobrien	return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
9389857Sobrien}
9489857Sobrien
9589857Sobrienstruct trail *
9689857Sobrientrail_new(const char *dirname, bool create)
9789857Sobrien{
9889857Sobrien	struct trail *trail;
9989857Sobrien
10089857Sobrien	trail = calloc(1, sizeof(*trail));
10189857Sobrien
10289857Sobrien	if (strlcpy(trail->tr_dirname, dirname, sizeof(trail->tr_dirname)) >=
10389857Sobrien	    sizeof(trail->tr_dirname)) {
10489857Sobrien		free(trail);
10589857Sobrien		pjdlog_error("Directory name too long (\"%s\").", dirname);
10689857Sobrien		errno = ENAMETOOLONG;
10789857Sobrien		return (NULL);
10889857Sobrien	}
10989857Sobrien	trail->tr_dirfp = opendir(dirname);
11089857Sobrien	if (trail->tr_dirfp == NULL) {
11189857Sobrien		if (create && errno == ENOENT) {
11289857Sobrien			if (mkdir(dirname, 0700) == -1) {
11389857Sobrien				pjdlog_errno(LOG_ERR,
11489857Sobrien				    "Unable to create directory \"%s\"",
11589857Sobrien				    dirname);
11689857Sobrien				free(trail);
11789857Sobrien				return (NULL);
11889857Sobrien			}
11989857Sobrien			/* TODO: Set directory ownership. */
12089857Sobrien		} else {
12189857Sobrien			pjdlog_errno(LOG_ERR,
12289857Sobrien			    "Unable to open directory \"%s\"",
12389857Sobrien			    dirname);
12489857Sobrien			free(trail);
12589857Sobrien			return (NULL);
12689857Sobrien		}
12789857Sobrien		trail->tr_dirfp = opendir(dirname);
12889857Sobrien		if (trail->tr_dirfp == NULL) {
12989857Sobrien			pjdlog_errno(LOG_ERR,
13089857Sobrien			    "Unable to open directory \"%s\"",
13189857Sobrien			    dirname);
13289857Sobrien			free(trail);
13389857Sobrien			return (NULL);
13489857Sobrien		}
13589857Sobrien	}
13689857Sobrien	trail->tr_filefd = -1;
13789857Sobrien	trail->tr_magic = TRAIL_MAGIC;
13889857Sobrien	return (trail);
13989857Sobrien}
14089857Sobrien
14189857Sobrienvoid
14289857Sobrientrail_free(struct trail *trail)
14389857Sobrien{
14489857Sobrien
14589857Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
14689857Sobrien
14789857Sobrien	if (trail->tr_filefd != -1)
14889857Sobrien		trail_close(trail);
14989857Sobrien	closedir(trail->tr_dirfp);
15089857Sobrien	bzero(trail, sizeof(*trail));
15191041Sobrien	trail->tr_magic = 0;
15291041Sobrien	trail->tr_filefd = -1;
15391041Sobrien	free(trail);
15491041Sobrien}
15591041Sobrien
15691041Sobrienstatic uint8_t
15789857Sobrientrail_type(DIR *dirfp, const char *filename)
15889857Sobrien{
15989857Sobrien	struct stat sb;
16089857Sobrien	int dfd;
16189857Sobrien
16291041Sobrien	PJDLOG_ASSERT(dirfp != NULL);
16391041Sobrien
16491041Sobrien	dfd = dirfd(dirfp);
16591041Sobrien	PJDLOG_ASSERT(dfd >= 0);
16691041Sobrien	if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
16791041Sobrien		pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
16891041Sobrien		return (DT_UNKNOWN);
16991041Sobrien	}
17091041Sobrien	return (IFTODT(sb.st_mode));
17189857Sobrien}
17289857Sobrien
17389857Sobrien/*
17489857Sobrien * Find trail file by first part of the name in case it was renamed.
17589857Sobrien * First part of the trail file name never changes, but trail file
17689857Sobrien * can be renamed when hosts are disconnected from .not_terminated
17789857Sobrien * to .[0-9]{14} or to .crash_recovery.
17889857Sobrien */
17989857Sobrienstatic bool
18089857Sobrientrail_find(struct trail *trail)
18189857Sobrien{
18289857Sobrien	struct dirent *dp;
18389857Sobrien
18489857Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
18589857Sobrien	PJDLOG_ASSERT(trail_is_not_terminated(trail->tr_filename));
18689857Sobrien
18789857Sobrien	rewinddir(trail->tr_dirfp);
18889857Sobrien	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
18989857Sobrien		if (strncmp(dp->d_name, trail->tr_filename, HALF_LEN + 1) == 0)
19089857Sobrien			break;
19189857Sobrien	}
19289857Sobrien	if (dp == NULL)
19389857Sobrien		return (false);
19489857Sobrien	PJDLOG_VERIFY(strlcpy(trail->tr_filename, dp->d_name,
19589857Sobrien	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
19678828Sobrien	return (true);
19778828Sobrien}
19878828Sobrien
19978828Sobrien/*
20078828Sobrien * Open the given trail file and move pointer at the given offset, as this is
20178828Sobrien * where receiver finished the last time.
20278828Sobrien * If the file doesn't exist or the given offset is equal to the file size,
20378828Sobrien * move to the next trail file.
20478828Sobrien */
20578828Sobrienvoid
20678828Sobrientrail_start(struct trail *trail, const char *filename, off_t offset)
20789857Sobrien{
20889857Sobrien	struct stat sb;
20989857Sobrien	int dfd, fd;
21089857Sobrien
21189857Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
21277298Sobrien
21377298Sobrien	PJDLOG_VERIFY(strlcpy(trail->tr_filename, filename,
21477298Sobrien	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
21577298Sobrien	trail->tr_filefd = -1;
21677298Sobrien
21789857Sobrien	if (trail->tr_filename[0] == '\0') {
21889857Sobrien		PJDLOG_ASSERT(offset == 0);
21989857Sobrien		trail_next(trail);
22089857Sobrien		return;
22189857Sobrien	}
22289857Sobrien
22389857Sobrien	dfd = dirfd(trail->tr_dirfp);
22489857Sobrien	PJDLOG_ASSERT(dfd >= 0);
22577298Sobrienagain:
22677298Sobrien	fd = openat(dfd, trail->tr_filename, O_RDONLY);
22777298Sobrien	if (fd == -1) {
22877298Sobrien		if (errno == ENOENT &&
22977298Sobrien		    trail_is_not_terminated(trail->tr_filename) &&
23077298Sobrien		    trail_find(trail)) {
23177298Sobrien			/* File was renamed. Retry with new name. */
23277298Sobrien			pjdlog_debug(1,
23389857Sobrien			   "Trail file was renamed since last connection to \"%s/%s\".",
23477298Sobrien			   trail->tr_dirname, trail->tr_filename);
23589857Sobrien			goto again;
23689857Sobrien		} else if (errno == ENOENT) {
23789857Sobrien			/* File disappeared. */
23889857Sobrien			pjdlog_debug(1, "File \"%s/%s\" doesn't exist.",
23989857Sobrien			    trail->tr_dirname, trail->tr_filename);
24089857Sobrien		} else {
24189857Sobrien			pjdlog_errno(LOG_ERR,
24289857Sobrien			    "Unable to open file \"%s/%s\", skipping",
24389857Sobrien			    trail->tr_dirname, trail->tr_filename);
24477298Sobrien		}
24577298Sobrien		trail_next(trail);
24677298Sobrien		return;
24789857Sobrien	}
24889857Sobrien	if (fstat(fd, &sb) == -1) {
24989857Sobrien		pjdlog_errno(LOG_ERR,
25089857Sobrien		    "Unable to stat file \"%s/%s\", skipping",
25189857Sobrien		    trail->tr_dirname, trail->tr_filename);
25289857Sobrien		close(fd);
25389857Sobrien		trail_next(trail);
25489857Sobrien		return;
25589857Sobrien	}
25689857Sobrien	if (!S_ISREG(sb.st_mode)) {
25789857Sobrien		pjdlog_warning("File \"%s/%s\" is not a regular file, skipping.",
25889857Sobrien		    trail->tr_dirname, trail->tr_filename);
25989857Sobrien		close(fd);
26089857Sobrien		trail_next(trail);
26189857Sobrien		return;
26277298Sobrien	}
26377298Sobrien	/*
26477298Sobrien	 * We continue sending requested file if:
26577298Sobrien	 * 1. It is not fully sent yet, or
26677298Sobrien	 * 2. It is fully sent, but is not terminated, so new data can be
26777298Sobrien	 *    appended still, or
26877298Sobrien	 * 3. It is fully sent but file name has changed.
26977298Sobrien	 *
27077298Sobrien	 * Note that we are fine if our .not_terminated or .crash_recovery file
27177298Sobrien	 * is smaller than the one on the receiver side, as it is possible that
27277298Sobrien	 * more data was send to the receiver than was safely stored on disk.
27389857Sobrien	 * We accept .not_terminated only because auditdistd can start before
27477298Sobrien	 * auditd manage to rename it to .crash_recovery.
27577298Sobrien	 */
27677298Sobrien	if (offset < sb.st_size ||
27777298Sobrien	    (offset >= sb.st_size &&
27877298Sobrien	     trail_is_not_terminated(trail->tr_filename)) ||
27977298Sobrien	    (offset >= sb.st_size && trail_is_not_terminated(filename) &&
28077298Sobrien	     trail_is_crash_recovery(trail->tr_filename))) {
28177298Sobrien		/* File was not fully send. Let's finish it. */
28277298Sobrien		if (lseek(fd, offset, SEEK_SET) == -1) {
28377298Sobrien			pjdlog_errno(LOG_ERR,
28477298Sobrien			    "Unable to move to offset %jd within file \"%s/%s\", skipping",
28577298Sobrien			    (intmax_t)offset, trail->tr_dirname,
28677298Sobrien			    trail->tr_filename);
28777298Sobrien			close(fd);
28877298Sobrien			trail_next(trail);
28977298Sobrien			return;
29077298Sobrien		}
29177298Sobrien		if (!trail_is_crash_recovery(trail->tr_filename)) {
29277298Sobrien			pjdlog_debug(1,
29377298Sobrien			    "Restarting file \"%s/%s\" at offset %jd.",
29477298Sobrien			    trail->tr_dirname, trail->tr_filename,
29577298Sobrien			    (intmax_t)offset);
29677298Sobrien		}
29777298Sobrien		trail->tr_filefd = fd;
29877298Sobrien		return;
29977298Sobrien	}
30077298Sobrien	close(fd);
30177298Sobrien	if (offset > sb.st_size) {
30277298Sobrien		pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
30377298Sobrien		    trail->tr_dirname, trail->tr_filename);
30477298Sobrien	} else {
30577298Sobrien		pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
30677298Sobrien		    trail->tr_dirname, trail->tr_filename);
30777298Sobrien	}
30877298Sobrien	/* Entire file is already sent or it shirnked, we can remove it. */
30977298Sobrien	if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
31077298Sobrien		pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
31177298Sobrien		    trail->tr_dirname, trail->tr_filename);
31277298Sobrien	}
31377298Sobrien	trail_next(trail);
31477298Sobrien}
31577298Sobrien
31677298Sobrien/*
31777298Sobrien * Set next file in the trail->tr_dirname directory and open it for reading.
31877298Sobrien */
31977298Sobrienvoid
32077298Sobrientrail_next(struct trail *trail)
32177298Sobrien{
32277298Sobrien	char curfile[PATH_MAX];
32377298Sobrien	struct dirent *dp;
32477298Sobrien	int dfd;
32577298Sobrien
32677298Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
32777298Sobrien	PJDLOG_ASSERT(trail->tr_filefd == -1);
32889857Sobrien
32977298Sobrienagain:
33077298Sobrien	curfile[0] = '\0';
33177298Sobrien
33277298Sobrien	rewinddir(trail->tr_dirfp);
33377298Sobrien	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
33477298Sobrien		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
33577298Sobrien			continue;
33677298Sobrien		if (dp->d_type == DT_UNKNOWN)
33777298Sobrien			dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
33877298Sobrien		/* We are only interested in regular files, skip the rest. */
33977298Sobrien		if (dp->d_type != DT_REG) {
34077298Sobrien			pjdlog_debug(1,
34177298Sobrien			    "File \"%s/%s\" is not a regular file, skipping.",
34277298Sobrien			    trail->tr_dirname, dp->d_name);
34377298Sobrien			continue;
34477298Sobrien		}
34577298Sobrien		/* Skip all files "greater" than curfile. */
34677298Sobrien		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
34777298Sobrien			continue;
34877298Sobrien		/* Skip all files "smaller" than the current trail_filename. */
34977298Sobrien		if (trail->tr_filename[0] != '\0' &&
35077298Sobrien		    strcmp(dp->d_name, trail->tr_filename) <= 0) {
35177298Sobrien			continue;
35277298Sobrien		}
35389857Sobrien		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
35477298Sobrien		    sizeof(curfile));
35577298Sobrien	}
35677298Sobrien	if (curfile[0] == '\0') {
35777298Sobrien		/*
35877298Sobrien		 * There are no new trail files, so we return.
35977298Sobrien		 * We don't clear trail_filename string, to know where to
36077298Sobrien		 * start when new file appears.
36189857Sobrien		 */
36277298Sobrien		PJDLOG_ASSERT(trail->tr_filefd == -1);
36377298Sobrien		pjdlog_debug(1, "No new trail files.");
36477298Sobrien		return;
36577298Sobrien	}
36677298Sobrien	PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
36777298Sobrien	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
36877298Sobrien	dfd = dirfd(trail->tr_dirfp);
36977298Sobrien	PJDLOG_ASSERT(dfd >= 0);
37077298Sobrien	trail->tr_filefd = openat(dfd, trail->tr_filename, O_RDONLY);
37177298Sobrien	if (trail->tr_filefd == -1) {
37277298Sobrien		pjdlog_errno(LOG_ERR,
37377298Sobrien		    "Unable to open file \"%s/%s\", skipping",
37477298Sobrien		    trail->tr_dirname, trail->tr_filename);
37589857Sobrien		goto again;
37677298Sobrien	}
37777298Sobrien	pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
37889857Sobrien	    trail->tr_filename);
37977298Sobrien}
38077298Sobrien
38177298Sobrien/*
38289857Sobrien * Close current trial file.
38377298Sobrien */
38489857Sobrienvoid
38589857Sobrientrail_close(struct trail *trail)
38677298Sobrien{
38789857Sobrien
38889857Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
38989857Sobrien	PJDLOG_ASSERT(trail->tr_filefd >= 0);
39077298Sobrien	PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
39177298Sobrien
39277298Sobrien	PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
39377298Sobrien	trail->tr_filefd = -1;
39477298Sobrien}
39577298Sobrien
39677298Sobrien/*
39777298Sobrien * Reset trail state. Used when connection is disconnected and we will
39877298Sobrien * need to start over after reconnect. Trail needs to be already closed.
39977298Sobrien */
40077298Sobrienvoid
40177298Sobrientrail_reset(struct trail *trail)
40277298Sobrien{
40377298Sobrien
40478828Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
40578828Sobrien	PJDLOG_ASSERT(trail->tr_filefd == -1);
40678828Sobrien
40778828Sobrien	trail->tr_filename[0] = '\0';
40877298Sobrien}
40977298Sobrien
41077298Sobrien/*
41177298Sobrien * Unlink current trial file.
41277298Sobrien */
41377298Sobrienvoid
41477298Sobrientrail_unlink(struct trail *trail, const char *filename)
41577298Sobrien{
41677298Sobrien	int dfd;
41777298Sobrien
41878828Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
41978828Sobrien	PJDLOG_ASSERT(filename != NULL);
42078828Sobrien	PJDLOG_ASSERT(filename[0] != '\0');
42178828Sobrien
42278828Sobrien	dfd = dirfd(trail->tr_dirfp);
42378828Sobrien	PJDLOG_ASSERT(dfd >= 0);
42478828Sobrien
42578828Sobrien	if (unlinkat(dfd, filename, 0) == -1) {
42678828Sobrien		pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
42778828Sobrien		    trail->tr_dirname, filename);
42878828Sobrien	} else {
42978828Sobrien		pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
43078828Sobrien		    trail->tr_dirname, filename);
43178828Sobrien	}
43278828Sobrien}
43378828Sobrien
43478828Sobrien/*
43578828Sobrien * Return true if we should switch to next trail file.
43678828Sobrien * We don't switch if our file name ends with ".not_terminated" and it
43778828Sobrien * exists (ie. wasn't renamed).
43878828Sobrien */
43978828Sobrienbool
44078828Sobrientrail_switch(struct trail *trail)
44177298Sobrien{
44277298Sobrien	char filename[PATH_MAX];
44377298Sobrien	int fd;
44477298Sobrien
44577298Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
44677298Sobrien	PJDLOG_ASSERT(trail->tr_filefd >= 0);
44777298Sobrien
44877298Sobrien	if (!trail_is_not_terminated(trail->tr_filename))
44977298Sobrien		return (true);
45077298Sobrien	fd = dirfd(trail->tr_dirfp);
45177298Sobrien	PJDLOG_ASSERT(fd >= 0);
45277298Sobrien	if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
45377298Sobrien		return (false);
45477298Sobrien	if (errno != ENOENT) {
45577298Sobrien		pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
45677298Sobrien		    trail->tr_dirname, trail->tr_filename);
45777298Sobrien	}
45877298Sobrien	strlcpy(filename, trail->tr_filename, sizeof(filename));
45977298Sobrien	if (!trail_find(trail)) {
46077298Sobrien		pjdlog_error("Trail file \"%s/%s\" disappeared.",
46177298Sobrien		    trail->tr_dirname, trail->tr_filename);
46277298Sobrien		return (true);
46377298Sobrien	}
46477298Sobrien	pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
46577298Sobrien	    trail->tr_dirname, filename, trail->tr_dirname,
46677298Sobrien	    trail->tr_filename);
46777298Sobrien	return (true);
46877298Sobrien}
46977298Sobrien
47061843Sobrienconst char *
47161843Sobrientrail_filename(const struct trail *trail)
47261843Sobrien{
47361843Sobrien
47477298Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
47561843Sobrien
47677298Sobrien	return (trail->tr_filename);
47761843Sobrien}
47877298Sobrien
47977298Sobrienint
48077298Sobrientrail_filefd(const struct trail *trail)
48177298Sobrien{
48277298Sobrien
48377298Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
48477298Sobrien
48577298Sobrien	return (trail->tr_filefd);
48677298Sobrien}
48777298Sobrien
48877298Sobrienint
48977298Sobrientrail_dirfd(const struct trail *trail)
49077298Sobrien{
49177298Sobrien
49277298Sobrien	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
49377298Sobrien
49477298Sobrien	return (dirfd(trail->tr_dirfp));
49577298Sobrien}
49661843Sobrien
49761843Sobrien/*
49861843Sobrien * Find the last file in the directory opened under dirfp.
49977298Sobrien */
50061843Sobrienvoid
50177298Sobrientrail_last(DIR *dirfp, char *filename, size_t filenamesize)
50261843Sobrien{
50377298Sobrien	char curfile[PATH_MAX];
50477298Sobrien	struct dirent *dp;
50577298Sobrien
50677298Sobrien	PJDLOG_ASSERT(dirfp != NULL);
50777298Sobrien
50877298Sobrien	curfile[0] = '\0';
50977298Sobrien
51077298Sobrien	rewinddir(dirfp);
51177298Sobrien	while ((dp = readdir(dirfp)) != NULL) {
51277298Sobrien		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
51377298Sobrien			continue;
51477298Sobrien		if (dp->d_type == DT_UNKNOWN)
51577298Sobrien			dp->d_type = trail_type(dirfp, dp->d_name);
51677298Sobrien		/* We are only interested in regular files, skip the rest. */
51777298Sobrien		if (dp->d_type != DT_REG)
51877298Sobrien			continue;
51977298Sobrien		/* Skip all files "greater" than curfile. */
52077298Sobrien		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
52177298Sobrien			continue;
52277298Sobrien		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
52377298Sobrien		    sizeof(curfile));
52477298Sobrien	}
52577298Sobrien	if (curfile[0] == '\0') {
52677298Sobrien		/*
52760484Sobrien		 * There are no trail files, so we return.
52860484Sobrien		 */
52960484Sobrien		pjdlog_debug(1, "No trail files.");
53060484Sobrien		bzero(filename, filenamesize);
53160484Sobrien		return;
53260484Sobrien	}
53360484Sobrien	PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
53460484Sobrien	pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
53560484Sobrien}
53660484Sobrien
53760484Sobrien/*
53860484Sobrien * Check if the given file name is a valid audit trail file name.
53960484Sobrien * Possible names:
54060484Sobrien * 20120106132657.20120106132805
54160484Sobrien * 20120106132657.not_terminated
54260484Sobrien * 20120106132657.crash_recovery
54360484Sobrien * If two names are given, check if the first name can be renamed
54460484Sobrien * to the second name. When renaming, first part of the name has
54560484Sobrien * to be identical and only the following renames are valid:
54660484Sobrien * 20120106132657.not_terminated -> 20120106132657.20120106132805
54760484Sobrien * 20120106132657.not_terminated -> 20120106132657.crash_recovery
54860484Sobrien */
54960484Sobrienbool
55060484Sobrientrail_validate_name(const char *srcname, const char *dstname)
55160484Sobrien{
55260484Sobrien	int i;
55360484Sobrien
55460484Sobrien	PJDLOG_ASSERT(srcname != NULL);
55560484Sobrien
55660484Sobrien	if (strlen(srcname) != 2 * HALF_LEN + 1)
55760484Sobrien		return (false);
55860484Sobrien	if (srcname[HALF_LEN] != '.')
55960484Sobrien		return (false);
56060484Sobrien	for (i = 0; i < HALF_LEN; i++) {
56160484Sobrien		if (srcname[i] < '0' || srcname[i] > '9')
56260484Sobrien			return (false);
56360484Sobrien	}
56460484Sobrien	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
56560484Sobrien		if (srcname[i] < '0' || srcname[i] > '9')
56660484Sobrien			break;
56760484Sobrien	}
56860484Sobrien	if (i < 2 * HALF_LEN - 1 &&
56989857Sobrien	    strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
57060484Sobrien	    strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
57160484Sobrien		return (false);
57260484Sobrien	}
57360484Sobrien
57460484Sobrien	if (dstname == NULL)
57560484Sobrien		return (true);
57660484Sobrien
57760484Sobrien	/* We tolarate if both names are identical. */
57878828Sobrien	if (strcmp(srcname, dstname) == 0)
57978828Sobrien		return (true);
58078828Sobrien
58178828Sobrien	/* We can only rename not_terminated files. */
58278828Sobrien	if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
58360484Sobrien		return (false);
58460484Sobrien	if (strlen(dstname) != 2 * HALF_LEN + 1)
58560484Sobrien		return (false);
58660484Sobrien	if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
58760484Sobrien		return (false);
58860484Sobrien	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
58960484Sobrien		if (dstname[i] < '0' || dstname[i] > '9')
59060484Sobrien			break;
59160484Sobrien	}
59260484Sobrien	if (i < 2 * HALF_LEN - 1 &&
59360484Sobrien	    strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
59460484Sobrien		return (false);
59560484Sobrien	}
59660484Sobrien
59760484Sobrien	return (true);
59860484Sobrien}
59960484Sobrien
60060484Sobrienint
60160484Sobrientrail_name_compare(const char *name0, const char *name1)
60260484Sobrien{
60360484Sobrien	int ret;
60460484Sobrien
60560484Sobrien	ret = strcmp(name0, name1);
60660484Sobrien	if (ret == 0)
60760484Sobrien		return (TRAIL_IDENTICAL);
60860484Sobrien	if (strncmp(name0, name1, HALF_LEN + 1) == 0)
60960484Sobrien		return (TRAIL_RENAMED);
61060484Sobrien	return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);
61160484Sobrien}
61260484Sobrien