1243730Srwatson/*-
2243730Srwatson * Copyright (c) 2012 The FreeBSD Foundation
3243730Srwatson * All rights reserved.
4243730Srwatson *
5243730Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from
6243730Srwatson * the FreeBSD Foundation.
7243730Srwatson *
8243730Srwatson * Redistribution and use in source and binary forms, with or without
9243730Srwatson * modification, are permitted provided that the following conditions
10243730Srwatson * are met:
11243730Srwatson * 1. Redistributions of source code must retain the above copyright
12243730Srwatson *    notice, this list of conditions and the following disclaimer.
13243730Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14243730Srwatson *    notice, this list of conditions and the following disclaimer in the
15243730Srwatson *    documentation and/or other materials provided with the distribution.
16243730Srwatson *
17243730Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18243730Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19243730Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20243730Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21243730Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22243730Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23243730Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24243730Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25243730Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26243730Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27243730Srwatson * SUCH DAMAGE.
28243730Srwatson */
29243730Srwatson
30243734Srwatson#include <config/config.h>
31243730Srwatson
32243730Srwatson#include <sys/param.h>
33243730Srwatson#include <sys/stat.h>
34243730Srwatson
35243730Srwatson#include <dirent.h>
36243730Srwatson#include <errno.h>
37243730Srwatson#include <fcntl.h>
38243730Srwatson#include <stdbool.h>
39243730Srwatson#include <stdint.h>
40243730Srwatson#include <stdlib.h>
41243730Srwatson#include <string.h>
42243730Srwatson#include <unistd.h>
43243730Srwatson
44243730Srwatson#include <compat/compat.h>
45243730Srwatson#ifndef HAVE_STRLCPY
46243730Srwatson#include <compat/strlcpy.h>
47243730Srwatson#endif
48243730Srwatson#ifndef HAVE_FACCESSAT
49243730Srwatson#include "faccessat.h"
50243730Srwatson#endif
51243730Srwatson#ifndef HAVE_FSTATAT
52243730Srwatson#include "fstatat.h"
53243730Srwatson#endif
54243730Srwatson#ifndef HAVE_OPENAT
55243730Srwatson#include "openat.h"
56243730Srwatson#endif
57243730Srwatson#ifndef HAVE_UNLINKAT
58243730Srwatson#include "unlinkat.h"
59243730Srwatson#endif
60243730Srwatson
61243734Srwatson#include "pjdlog.h"
62243730Srwatson#include "trail.h"
63243730Srwatson
64243730Srwatson#define	TRAIL_MAGIC	0x79a11
65243730Srwatsonstruct trail {
66243730Srwatson	int	 tr_magic;
67243730Srwatson	/* Path usually to /var/audit/dist/ directory. */
68243730Srwatson	char	 tr_dirname[PATH_MAX];
69243730Srwatson	/* Descriptor to td_dirname directory. */
70243730Srwatson	DIR	*tr_dirfp;
71243730Srwatson	/* Path to audit trail file. */
72243730Srwatson	char	 tr_filename[PATH_MAX];
73243730Srwatson	/* Descriptor to audit trail file. */
74243730Srwatson	int	 tr_filefd;
75243730Srwatson};
76243730Srwatson
77243730Srwatson#define	HALF_LEN	14
78243730Srwatson
79243730Srwatsonbool
80243730Srwatsontrail_is_not_terminated(const char *filename)
81243730Srwatson{
82243730Srwatson
83243730Srwatson	return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
84243730Srwatson}
85243730Srwatson
86243730Srwatsonbool
87243730Srwatsontrail_is_crash_recovery(const char *filename)
88243730Srwatson{
89243730Srwatson
90243730Srwatson	return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
91243730Srwatson}
92243730Srwatson
93243730Srwatsonstruct trail *
94243730Srwatsontrail_new(const char *dirname, bool create)
95243730Srwatson{
96243730Srwatson	struct trail *trail;
97243730Srwatson
98243730Srwatson	trail = calloc(1, sizeof(*trail));
99243730Srwatson
100243730Srwatson	if (strlcpy(trail->tr_dirname, dirname, sizeof(trail->tr_dirname)) >=
101243730Srwatson	    sizeof(trail->tr_dirname)) {
102243730Srwatson		free(trail);
103243730Srwatson		pjdlog_error("Directory name too long (\"%s\").", dirname);
104243730Srwatson		errno = ENAMETOOLONG;
105243730Srwatson		return (NULL);
106243730Srwatson	}
107243730Srwatson	trail->tr_dirfp = opendir(dirname);
108243730Srwatson	if (trail->tr_dirfp == NULL) {
109243730Srwatson		if (create && errno == ENOENT) {
110243730Srwatson			if (mkdir(dirname, 0700) == -1) {
111243730Srwatson				pjdlog_errno(LOG_ERR,
112243730Srwatson				    "Unable to create directory \"%s\"",
113243730Srwatson				    dirname);
114243730Srwatson				free(trail);
115243730Srwatson				return (NULL);
116243730Srwatson			}
117243730Srwatson			/* TODO: Set directory ownership. */
118243730Srwatson		} else {
119243730Srwatson			pjdlog_errno(LOG_ERR,
120243730Srwatson			    "Unable to open directory \"%s\"",
121243730Srwatson			    dirname);
122243730Srwatson			free(trail);
123243730Srwatson			return (NULL);
124243730Srwatson		}
125243730Srwatson		trail->tr_dirfp = opendir(dirname);
126243730Srwatson		if (trail->tr_dirfp == NULL) {
127243730Srwatson			pjdlog_errno(LOG_ERR,
128243730Srwatson			    "Unable to open directory \"%s\"",
129243730Srwatson			    dirname);
130243730Srwatson			free(trail);
131243730Srwatson			return (NULL);
132243730Srwatson		}
133243730Srwatson	}
134243730Srwatson	trail->tr_filefd = -1;
135243730Srwatson	trail->tr_magic = TRAIL_MAGIC;
136243730Srwatson	return (trail);
137243730Srwatson}
138243730Srwatson
139243730Srwatsonvoid
140243730Srwatsontrail_free(struct trail *trail)
141243730Srwatson{
142243730Srwatson
143243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
144243730Srwatson
145243730Srwatson	if (trail->tr_filefd != -1)
146243730Srwatson		trail_close(trail);
147243730Srwatson	closedir(trail->tr_dirfp);
148243730Srwatson	bzero(trail, sizeof(*trail));
149243730Srwatson	trail->tr_magic = 0;
150243730Srwatson	trail->tr_filefd = -1;
151243730Srwatson	free(trail);
152243730Srwatson}
153243730Srwatson
154243730Srwatsonstatic uint8_t
155243730Srwatsontrail_type(DIR *dirfp, const char *filename)
156243730Srwatson{
157243730Srwatson	struct stat sb;
158243730Srwatson	int dfd;
159243730Srwatson
160243730Srwatson	PJDLOG_ASSERT(dirfp != NULL);
161243730Srwatson
162243730Srwatson	dfd = dirfd(dirfp);
163243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
164243730Srwatson	if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
165243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
166243730Srwatson		return (DT_UNKNOWN);
167243730Srwatson	}
168243730Srwatson	return (IFTODT(sb.st_mode));
169243730Srwatson}
170243730Srwatson
171243730Srwatson/*
172243730Srwatson * Find trail file by first part of the name in case it was renamed.
173243730Srwatson * First part of the trail file name never changes, but trail file
174243730Srwatson * can be renamed when hosts are disconnected from .not_terminated
175243730Srwatson * to .[0-9]{14} or to .crash_recovery.
176243730Srwatson */
177243730Srwatsonstatic bool
178243730Srwatsontrail_find(struct trail *trail)
179243730Srwatson{
180243730Srwatson	struct dirent *dp;
181243730Srwatson
182243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
183243730Srwatson	PJDLOG_ASSERT(trail_is_not_terminated(trail->tr_filename));
184243730Srwatson
185243730Srwatson	rewinddir(trail->tr_dirfp);
186243730Srwatson	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
187243730Srwatson		if (strncmp(dp->d_name, trail->tr_filename, HALF_LEN + 1) == 0)
188243730Srwatson			break;
189243730Srwatson	}
190243730Srwatson	if (dp == NULL)
191243730Srwatson		return (false);
192243730Srwatson	PJDLOG_VERIFY(strlcpy(trail->tr_filename, dp->d_name,
193243730Srwatson	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
194243730Srwatson	return (true);
195243730Srwatson}
196243730Srwatson
197243730Srwatson/*
198243730Srwatson * Open the given trail file and move pointer at the given offset, as this is
199243730Srwatson * where receiver finished the last time.
200243730Srwatson * If the file doesn't exist or the given offset is equal to the file size,
201243730Srwatson * move to the next trail file.
202243730Srwatson */
203243730Srwatsonvoid
204243730Srwatsontrail_start(struct trail *trail, const char *filename, off_t offset)
205243730Srwatson{
206243730Srwatson	struct stat sb;
207243730Srwatson	int dfd, fd;
208243730Srwatson
209243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
210243730Srwatson
211243730Srwatson	PJDLOG_VERIFY(strlcpy(trail->tr_filename, filename,
212243730Srwatson	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
213243730Srwatson	trail->tr_filefd = -1;
214243730Srwatson
215243730Srwatson	if (trail->tr_filename[0] == '\0') {
216243730Srwatson		PJDLOG_ASSERT(offset == 0);
217243730Srwatson		trail_next(trail);
218243730Srwatson		return;
219243730Srwatson	}
220243730Srwatson
221243730Srwatson	dfd = dirfd(trail->tr_dirfp);
222243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
223243730Srwatsonagain:
224243730Srwatson	fd = openat(dfd, trail->tr_filename, O_RDONLY);
225243730Srwatson	if (fd == -1) {
226243730Srwatson		if (errno == ENOENT &&
227243730Srwatson		    trail_is_not_terminated(trail->tr_filename) &&
228243730Srwatson		    trail_find(trail)) {
229243730Srwatson			/* File was renamed. Retry with new name. */
230243730Srwatson			pjdlog_debug(1,
231243730Srwatson			   "Trail file was renamed since last connection to \"%s/%s\".",
232243730Srwatson			   trail->tr_dirname, trail->tr_filename);
233243730Srwatson			goto again;
234243730Srwatson		} else if (errno == ENOENT) {
235243730Srwatson			/* File disappeared. */
236243730Srwatson			pjdlog_debug(1, "File \"%s/%s\" doesn't exist.",
237243730Srwatson			    trail->tr_dirname, trail->tr_filename);
238243730Srwatson		} else {
239243730Srwatson			pjdlog_errno(LOG_ERR,
240243730Srwatson			    "Unable to open file \"%s/%s\", skipping",
241243730Srwatson			    trail->tr_dirname, trail->tr_filename);
242243730Srwatson		}
243243730Srwatson		trail_next(trail);
244243730Srwatson		return;
245243730Srwatson	}
246243730Srwatson	if (fstat(fd, &sb) == -1) {
247243730Srwatson		pjdlog_errno(LOG_ERR,
248243730Srwatson		    "Unable to stat file \"%s/%s\", skipping",
249243730Srwatson		    trail->tr_dirname, trail->tr_filename);
250243730Srwatson		close(fd);
251243730Srwatson		trail_next(trail);
252243730Srwatson		return;
253243730Srwatson	}
254243730Srwatson	if (!S_ISREG(sb.st_mode)) {
255243730Srwatson		pjdlog_warning("File \"%s/%s\" is not a regular file, skipping.",
256243730Srwatson		    trail->tr_dirname, trail->tr_filename);
257243730Srwatson		close(fd);
258243730Srwatson		trail_next(trail);
259243730Srwatson		return;
260243730Srwatson	}
261243730Srwatson	/*
262243730Srwatson	 * We continue sending requested file if:
263243730Srwatson	 * 1. It is not fully sent yet, or
264243730Srwatson	 * 2. It is fully sent, but is not terminated, so new data can be
265243730Srwatson	 *    appended still, or
266243730Srwatson	 * 3. It is fully sent but file name has changed.
267243730Srwatson	 *
268243730Srwatson	 * Note that we are fine if our .not_terminated or .crash_recovery file
269243730Srwatson	 * is smaller than the one on the receiver side, as it is possible that
270243730Srwatson	 * more data was send to the receiver than was safely stored on disk.
271243730Srwatson	 * We accept .not_terminated only because auditdistd can start before
272243730Srwatson	 * auditd manage to rename it to .crash_recovery.
273243730Srwatson	 */
274243730Srwatson	if (offset < sb.st_size ||
275243730Srwatson	    (offset >= sb.st_size &&
276243730Srwatson	     trail_is_not_terminated(trail->tr_filename)) ||
277243730Srwatson	    (offset >= sb.st_size && trail_is_not_terminated(filename) &&
278243730Srwatson	     trail_is_crash_recovery(trail->tr_filename))) {
279243730Srwatson		/* File was not fully send. Let's finish it. */
280243730Srwatson		if (lseek(fd, offset, SEEK_SET) == -1) {
281243730Srwatson			pjdlog_errno(LOG_ERR,
282243730Srwatson			    "Unable to move to offset %jd within file \"%s/%s\", skipping",
283243730Srwatson			    (intmax_t)offset, trail->tr_dirname,
284243730Srwatson			    trail->tr_filename);
285243730Srwatson			close(fd);
286243730Srwatson			trail_next(trail);
287243730Srwatson			return;
288243730Srwatson		}
289243730Srwatson		if (!trail_is_crash_recovery(trail->tr_filename)) {
290243730Srwatson			pjdlog_debug(1,
291243730Srwatson			    "Restarting file \"%s/%s\" at offset %jd.",
292243730Srwatson			    trail->tr_dirname, trail->tr_filename,
293243730Srwatson			    (intmax_t)offset);
294243730Srwatson		}
295243730Srwatson		trail->tr_filefd = fd;
296243730Srwatson		return;
297243730Srwatson	}
298243730Srwatson	close(fd);
299243730Srwatson	if (offset > sb.st_size) {
300243730Srwatson		pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
301243730Srwatson		    trail->tr_dirname, trail->tr_filename);
302243730Srwatson	} else {
303243730Srwatson		pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
304243730Srwatson		    trail->tr_dirname, trail->tr_filename);
305243730Srwatson	}
306243730Srwatson	/* Entire file is already sent or it shirnked, we can remove it. */
307243730Srwatson	if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
308243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
309243730Srwatson		    trail->tr_dirname, trail->tr_filename);
310243730Srwatson	}
311243730Srwatson	trail_next(trail);
312243730Srwatson}
313243730Srwatson
314243730Srwatson/*
315243730Srwatson * Set next file in the trail->tr_dirname directory and open it for reading.
316243730Srwatson */
317243730Srwatsonvoid
318243730Srwatsontrail_next(struct trail *trail)
319243730Srwatson{
320243730Srwatson	char curfile[PATH_MAX];
321243730Srwatson	struct dirent *dp;
322243730Srwatson	int dfd;
323243730Srwatson
324243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
325243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd == -1);
326243730Srwatson
327243730Srwatsonagain:
328243730Srwatson	curfile[0] = '\0';
329243730Srwatson
330243730Srwatson	rewinddir(trail->tr_dirfp);
331243730Srwatson	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
332243730Srwatson		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
333243730Srwatson			continue;
334243730Srwatson		if (dp->d_type == DT_UNKNOWN)
335243730Srwatson			dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
336243730Srwatson		/* We are only interested in regular files, skip the rest. */
337243730Srwatson		if (dp->d_type != DT_REG) {
338243730Srwatson			pjdlog_debug(1,
339243730Srwatson			    "File \"%s/%s\" is not a regular file, skipping.",
340243730Srwatson			    trail->tr_dirname, dp->d_name);
341243730Srwatson			continue;
342243730Srwatson		}
343243730Srwatson		/* Skip all files "greater" than curfile. */
344243730Srwatson		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
345243730Srwatson			continue;
346243730Srwatson		/* Skip all files "smaller" than the current trail_filename. */
347243730Srwatson		if (trail->tr_filename[0] != '\0' &&
348243730Srwatson		    strcmp(dp->d_name, trail->tr_filename) <= 0) {
349243730Srwatson			continue;
350243730Srwatson		}
351243730Srwatson		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
352243730Srwatson		    sizeof(curfile));
353243730Srwatson	}
354243730Srwatson	if (curfile[0] == '\0') {
355243730Srwatson		/*
356243730Srwatson		 * There are no new trail files, so we return.
357243730Srwatson		 * We don't clear trail_filename string, to know where to
358243730Srwatson		 * start when new file appears.
359243730Srwatson		 */
360243730Srwatson		PJDLOG_ASSERT(trail->tr_filefd == -1);
361243730Srwatson		pjdlog_debug(1, "No new trail files.");
362243730Srwatson		return;
363243730Srwatson	}
364243730Srwatson	PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
365243730Srwatson	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
366243730Srwatson	dfd = dirfd(trail->tr_dirfp);
367243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
368243730Srwatson	trail->tr_filefd = openat(dfd, trail->tr_filename, O_RDONLY);
369243730Srwatson	if (trail->tr_filefd == -1) {
370243730Srwatson		pjdlog_errno(LOG_ERR,
371243730Srwatson		    "Unable to open file \"%s/%s\", skipping",
372243730Srwatson		    trail->tr_dirname, trail->tr_filename);
373243730Srwatson		goto again;
374243730Srwatson	}
375243730Srwatson	pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
376243730Srwatson	    trail->tr_filename);
377243730Srwatson}
378243730Srwatson
379243730Srwatson/*
380243730Srwatson * Close current trial file.
381243730Srwatson */
382243730Srwatsonvoid
383243730Srwatsontrail_close(struct trail *trail)
384243730Srwatson{
385243730Srwatson
386243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
387243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd >= 0);
388243730Srwatson	PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
389243730Srwatson
390243730Srwatson	PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
391243730Srwatson	trail->tr_filefd = -1;
392243730Srwatson}
393243730Srwatson
394243730Srwatson/*
395243730Srwatson * Reset trail state. Used when connection is disconnected and we will
396243730Srwatson * need to start over after reconnect. Trail needs to be already closed.
397243730Srwatson */
398243730Srwatsonvoid
399243730Srwatsontrail_reset(struct trail *trail)
400243730Srwatson{
401243730Srwatson
402243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
403243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd == -1);
404243730Srwatson
405243730Srwatson	trail->tr_filename[0] = '\0';
406243730Srwatson}
407243730Srwatson
408243730Srwatson/*
409243730Srwatson * Unlink current trial file.
410243730Srwatson */
411243730Srwatsonvoid
412243730Srwatsontrail_unlink(struct trail *trail, const char *filename)
413243730Srwatson{
414243730Srwatson	int dfd;
415243730Srwatson
416243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
417243730Srwatson	PJDLOG_ASSERT(filename != NULL);
418243730Srwatson	PJDLOG_ASSERT(filename[0] != '\0');
419243730Srwatson
420243730Srwatson	dfd = dirfd(trail->tr_dirfp);
421243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
422243730Srwatson
423243730Srwatson	if (unlinkat(dfd, filename, 0) == -1) {
424243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
425243730Srwatson		    trail->tr_dirname, filename);
426243730Srwatson	} else {
427243730Srwatson		pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
428243730Srwatson		    trail->tr_dirname, filename);
429243730Srwatson	}
430243730Srwatson}
431243730Srwatson
432243730Srwatson/*
433243730Srwatson * Return true if we should switch to next trail file.
434243730Srwatson * We don't switch if our file name ends with ".not_terminated" and it
435243730Srwatson * exists (ie. wasn't renamed).
436243730Srwatson */
437243730Srwatsonbool
438243730Srwatsontrail_switch(struct trail *trail)
439243730Srwatson{
440243730Srwatson	char filename[PATH_MAX];
441243730Srwatson	int fd;
442243730Srwatson
443243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
444243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd >= 0);
445243730Srwatson
446243730Srwatson	if (!trail_is_not_terminated(trail->tr_filename))
447243730Srwatson		return (true);
448243730Srwatson	fd = dirfd(trail->tr_dirfp);
449243730Srwatson	PJDLOG_ASSERT(fd >= 0);
450243730Srwatson	if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
451243730Srwatson		return (false);
452243730Srwatson	if (errno != ENOENT) {
453243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
454243730Srwatson		    trail->tr_dirname, trail->tr_filename);
455243730Srwatson	}
456243730Srwatson	strlcpy(filename, trail->tr_filename, sizeof(filename));
457243730Srwatson	if (!trail_find(trail)) {
458243730Srwatson		pjdlog_error("Trail file \"%s/%s\" disappeared.",
459243730Srwatson		    trail->tr_dirname, trail->tr_filename);
460243730Srwatson		return (true);
461243730Srwatson	}
462243730Srwatson	pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
463243730Srwatson	    trail->tr_dirname, filename, trail->tr_dirname,
464243730Srwatson	    trail->tr_filename);
465243730Srwatson	return (true);
466243730Srwatson}
467243730Srwatson
468243730Srwatsonconst char *
469243730Srwatsontrail_filename(const struct trail *trail)
470243730Srwatson{
471243730Srwatson
472243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
473243730Srwatson
474243730Srwatson	return (trail->tr_filename);
475243730Srwatson}
476243730Srwatson
477243730Srwatsonint
478243730Srwatsontrail_filefd(const struct trail *trail)
479243730Srwatson{
480243730Srwatson
481243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
482243730Srwatson
483243730Srwatson	return (trail->tr_filefd);
484243730Srwatson}
485243730Srwatson
486243730Srwatsonint
487243730Srwatsontrail_dirfd(const struct trail *trail)
488243730Srwatson{
489243730Srwatson
490243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
491243730Srwatson
492243730Srwatson	return (dirfd(trail->tr_dirfp));
493243730Srwatson}
494243730Srwatson
495243730Srwatson/*
496243730Srwatson * Find the last file in the directory opened under dirfp.
497243730Srwatson */
498243730Srwatsonvoid
499243730Srwatsontrail_last(DIR *dirfp, char *filename, size_t filenamesize)
500243730Srwatson{
501243730Srwatson	char curfile[PATH_MAX];
502243730Srwatson	struct dirent *dp;
503243730Srwatson
504243730Srwatson	PJDLOG_ASSERT(dirfp != NULL);
505243730Srwatson
506243730Srwatson	curfile[0] = '\0';
507243730Srwatson
508243730Srwatson	rewinddir(dirfp);
509243730Srwatson	while ((dp = readdir(dirfp)) != NULL) {
510243730Srwatson		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
511243730Srwatson			continue;
512243730Srwatson		if (dp->d_type == DT_UNKNOWN)
513243730Srwatson			dp->d_type = trail_type(dirfp, dp->d_name);
514243730Srwatson		/* We are only interested in regular files, skip the rest. */
515243730Srwatson		if (dp->d_type != DT_REG)
516243730Srwatson			continue;
517243730Srwatson		/* Skip all files "greater" than curfile. */
518243730Srwatson		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
519243730Srwatson			continue;
520243730Srwatson		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
521243730Srwatson		    sizeof(curfile));
522243730Srwatson	}
523243730Srwatson	if (curfile[0] == '\0') {
524243730Srwatson		/*
525243730Srwatson		 * There are no trail files, so we return.
526243730Srwatson		 */
527243730Srwatson		pjdlog_debug(1, "No trail files.");
528243730Srwatson		bzero(filename, filenamesize);
529243730Srwatson		return;
530243730Srwatson	}
531243730Srwatson	PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
532243730Srwatson	pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
533243730Srwatson}
534243730Srwatson
535243730Srwatson/*
536243730Srwatson * Check if the given file name is a valid audit trail file name.
537243730Srwatson * Possible names:
538243730Srwatson * 20120106132657.20120106132805
539243730Srwatson * 20120106132657.not_terminated
540243730Srwatson * 20120106132657.crash_recovery
541243730Srwatson * If two names are given, check if the first name can be renamed
542243730Srwatson * to the second name. When renaming, first part of the name has
543243730Srwatson * to be identical and only the following renames are valid:
544243730Srwatson * 20120106132657.not_terminated -> 20120106132657.20120106132805
545243730Srwatson * 20120106132657.not_terminated -> 20120106132657.crash_recovery
546243730Srwatson */
547243730Srwatsonbool
548243730Srwatsontrail_validate_name(const char *srcname, const char *dstname)
549243730Srwatson{
550243730Srwatson	int i;
551243730Srwatson
552243730Srwatson	PJDLOG_ASSERT(srcname != NULL);
553243730Srwatson
554243730Srwatson	if (strlen(srcname) != 2 * HALF_LEN + 1)
555243730Srwatson		return (false);
556243730Srwatson	if (srcname[HALF_LEN] != '.')
557243730Srwatson		return (false);
558243730Srwatson	for (i = 0; i < HALF_LEN; i++) {
559243730Srwatson		if (srcname[i] < '0' || srcname[i] > '9')
560243730Srwatson			return (false);
561243730Srwatson	}
562243730Srwatson	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
563243730Srwatson		if (srcname[i] < '0' || srcname[i] > '9')
564243730Srwatson			break;
565243730Srwatson	}
566243730Srwatson	if (i < 2 * HALF_LEN - 1 &&
567243730Srwatson	    strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
568243730Srwatson	    strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
569243730Srwatson		return (false);
570243730Srwatson	}
571243730Srwatson
572243730Srwatson	if (dstname == NULL)
573243730Srwatson		return (true);
574243730Srwatson
575243730Srwatson	/* We tolarate if both names are identical. */
576243730Srwatson	if (strcmp(srcname, dstname) == 0)
577243730Srwatson		return (true);
578243730Srwatson
579243730Srwatson	/* We can only rename not_terminated files. */
580243730Srwatson	if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
581243730Srwatson		return (false);
582243730Srwatson	if (strlen(dstname) != 2 * HALF_LEN + 1)
583243730Srwatson		return (false);
584243730Srwatson	if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
585243730Srwatson		return (false);
586243730Srwatson	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
587243730Srwatson		if (dstname[i] < '0' || dstname[i] > '9')
588243730Srwatson			break;
589243730Srwatson	}
590243730Srwatson	if (i < 2 * HALF_LEN - 1 &&
591243730Srwatson	    strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
592243730Srwatson		return (false);
593243730Srwatson	}
594243730Srwatson
595243730Srwatson	return (true);
596243730Srwatson}
597243730Srwatson
598243730Srwatsonint
599243730Srwatsontrail_name_compare(const char *name0, const char *name1)
600243730Srwatson{
601243730Srwatson	int ret;
602243730Srwatson
603243730Srwatson	ret = strcmp(name0, name1);
604243730Srwatson	if (ret == 0)
605243730Srwatson		return (TRAIL_IDENTICAL);
606243730Srwatson	if (strncmp(name0, name1, HALF_LEN + 1) == 0)
607243730Srwatson		return (TRAIL_RENAMED);
608243730Srwatson	return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);
609243730Srwatson}
610