1aa772005SRobert Watson /*-
2aa772005SRobert Watson * Copyright (c) 2012 The FreeBSD Foundation
3aa772005SRobert Watson * All rights reserved.
4aa772005SRobert Watson *
5aa772005SRobert Watson * This software was developed by Pawel Jakub Dawidek under sponsorship from
6aa772005SRobert Watson * the FreeBSD Foundation.
7aa772005SRobert Watson *
8aa772005SRobert Watson * Redistribution and use in source and binary forms, with or without
9aa772005SRobert Watson * modification, are permitted provided that the following conditions
10aa772005SRobert Watson * are met:
11aa772005SRobert Watson * 1. Redistributions of source code must retain the above copyright
12aa772005SRobert Watson * notice, this list of conditions and the following disclaimer.
13aa772005SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
14aa772005SRobert Watson * notice, this list of conditions and the following disclaimer in the
15aa772005SRobert Watson * documentation and/or other materials provided with the distribution.
16aa772005SRobert Watson *
17aa772005SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18aa772005SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19aa772005SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20aa772005SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21aa772005SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22aa772005SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23aa772005SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24aa772005SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25aa772005SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26aa772005SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27aa772005SRobert Watson * SUCH DAMAGE.
28aa772005SRobert Watson */
29aa772005SRobert Watson
30aa772005SRobert Watson #include <config/config.h>
31aa772005SRobert Watson
32aa772005SRobert Watson #include <sys/param.h>
33aa772005SRobert Watson #include <sys/stat.h>
34aa772005SRobert Watson
35aa772005SRobert Watson #include <dirent.h>
36aa772005SRobert Watson #include <errno.h>
37aa772005SRobert Watson #include <fcntl.h>
38aa772005SRobert Watson #include <stdbool.h>
39aa772005SRobert Watson #include <stdint.h>
40aa772005SRobert Watson #include <stdlib.h>
41aa772005SRobert Watson #include <string.h>
42aa772005SRobert Watson #include <unistd.h>
43aa772005SRobert Watson
44aa772005SRobert Watson #include <compat/compat.h>
45aa772005SRobert Watson #ifndef HAVE_STRLCPY
46aa772005SRobert Watson #include <compat/strlcpy.h>
47aa772005SRobert Watson #endif
48aa772005SRobert Watson #ifndef HAVE_FACCESSAT
49aa772005SRobert Watson #include "faccessat.h"
50aa772005SRobert Watson #endif
51aa772005SRobert Watson #ifndef HAVE_FSTATAT
52aa772005SRobert Watson #include "fstatat.h"
53aa772005SRobert Watson #endif
54aa772005SRobert Watson #ifndef HAVE_OPENAT
55aa772005SRobert Watson #include "openat.h"
56aa772005SRobert Watson #endif
57aa772005SRobert Watson #ifndef HAVE_UNLINKAT
58aa772005SRobert Watson #include "unlinkat.h"
59aa772005SRobert Watson #endif
60aa772005SRobert Watson
61aa772005SRobert Watson #include "pjdlog.h"
62aa772005SRobert Watson #include "trail.h"
63aa772005SRobert Watson
64aa772005SRobert Watson #define TRAIL_MAGIC 0x79a11
65aa772005SRobert Watson struct trail {
66aa772005SRobert Watson int tr_magic;
67aa772005SRobert Watson /* Path usually to /var/audit/dist/ directory. */
68aa772005SRobert Watson char tr_dirname[PATH_MAX];
69aa772005SRobert Watson /* Descriptor to td_dirname directory. */
70aa772005SRobert Watson DIR *tr_dirfp;
71aa772005SRobert Watson /* Path to audit trail file. */
72aa772005SRobert Watson char tr_filename[PATH_MAX];
73aa772005SRobert Watson /* Descriptor to audit trail file. */
74aa772005SRobert Watson int tr_filefd;
75aa772005SRobert Watson };
76aa772005SRobert Watson
77aa772005SRobert Watson #define HALF_LEN 14
78aa772005SRobert Watson
79aa772005SRobert Watson bool
trail_is_not_terminated(const char * filename)80aa772005SRobert Watson trail_is_not_terminated(const char *filename)
81aa772005SRobert Watson {
82aa772005SRobert Watson
83aa772005SRobert Watson return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
84aa772005SRobert Watson }
85aa772005SRobert Watson
86aa772005SRobert Watson bool
trail_is_crash_recovery(const char * filename)87aa772005SRobert Watson trail_is_crash_recovery(const char *filename)
88aa772005SRobert Watson {
89aa772005SRobert Watson
90aa772005SRobert Watson return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
91aa772005SRobert Watson }
92aa772005SRobert Watson
93aa772005SRobert Watson struct trail *
trail_new(const char * dirname,bool create)94aa772005SRobert Watson trail_new(const char *dirname, bool create)
95aa772005SRobert Watson {
96aa772005SRobert Watson struct trail *trail;
97aa772005SRobert Watson
98aa772005SRobert Watson trail = calloc(1, sizeof(*trail));
99aa772005SRobert Watson
100aa772005SRobert Watson if (strlcpy(trail->tr_dirname, dirname, sizeof(trail->tr_dirname)) >=
101aa772005SRobert Watson sizeof(trail->tr_dirname)) {
102aa772005SRobert Watson free(trail);
103aa772005SRobert Watson pjdlog_error("Directory name too long (\"%s\").", dirname);
104aa772005SRobert Watson errno = ENAMETOOLONG;
105aa772005SRobert Watson return (NULL);
106aa772005SRobert Watson }
107aa772005SRobert Watson trail->tr_dirfp = opendir(dirname);
108aa772005SRobert Watson if (trail->tr_dirfp == NULL) {
109aa772005SRobert Watson if (create && errno == ENOENT) {
110aa772005SRobert Watson if (mkdir(dirname, 0700) == -1) {
111aa772005SRobert Watson pjdlog_errno(LOG_ERR,
112aa772005SRobert Watson "Unable to create directory \"%s\"",
113aa772005SRobert Watson dirname);
114aa772005SRobert Watson free(trail);
115aa772005SRobert Watson return (NULL);
116aa772005SRobert Watson }
117aa772005SRobert Watson /* TODO: Set directory ownership. */
118aa772005SRobert Watson } else {
119aa772005SRobert Watson pjdlog_errno(LOG_ERR,
120aa772005SRobert Watson "Unable to open directory \"%s\"",
121aa772005SRobert Watson dirname);
122aa772005SRobert Watson free(trail);
123aa772005SRobert Watson return (NULL);
124aa772005SRobert Watson }
125aa772005SRobert Watson trail->tr_dirfp = opendir(dirname);
126aa772005SRobert Watson if (trail->tr_dirfp == NULL) {
127aa772005SRobert Watson pjdlog_errno(LOG_ERR,
128aa772005SRobert Watson "Unable to open directory \"%s\"",
129aa772005SRobert Watson dirname);
130aa772005SRobert Watson free(trail);
131aa772005SRobert Watson return (NULL);
132aa772005SRobert Watson }
133aa772005SRobert Watson }
134aa772005SRobert Watson trail->tr_filefd = -1;
135aa772005SRobert Watson trail->tr_magic = TRAIL_MAGIC;
136aa772005SRobert Watson return (trail);
137aa772005SRobert Watson }
138aa772005SRobert Watson
139aa772005SRobert Watson void
trail_free(struct trail * trail)140aa772005SRobert Watson trail_free(struct trail *trail)
141aa772005SRobert Watson {
142aa772005SRobert Watson
143aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
144aa772005SRobert Watson
145aa772005SRobert Watson if (trail->tr_filefd != -1)
146aa772005SRobert Watson trail_close(trail);
147aa772005SRobert Watson closedir(trail->tr_dirfp);
148aa772005SRobert Watson bzero(trail, sizeof(*trail));
149aa772005SRobert Watson trail->tr_magic = 0;
150aa772005SRobert Watson trail->tr_filefd = -1;
151aa772005SRobert Watson free(trail);
152aa772005SRobert Watson }
153aa772005SRobert Watson
154aa772005SRobert Watson static uint8_t
trail_type(DIR * dirfp,const char * filename)155aa772005SRobert Watson trail_type(DIR *dirfp, const char *filename)
156aa772005SRobert Watson {
157aa772005SRobert Watson struct stat sb;
158aa772005SRobert Watson int dfd;
159aa772005SRobert Watson
160aa772005SRobert Watson PJDLOG_ASSERT(dirfp != NULL);
161aa772005SRobert Watson
162aa772005SRobert Watson dfd = dirfd(dirfp);
163aa772005SRobert Watson PJDLOG_ASSERT(dfd >= 0);
164aa772005SRobert Watson if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
165aa772005SRobert Watson pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
166aa772005SRobert Watson return (DT_UNKNOWN);
167aa772005SRobert Watson }
168aa772005SRobert Watson return (IFTODT(sb.st_mode));
169aa772005SRobert Watson }
170aa772005SRobert Watson
171aa772005SRobert Watson /*
172aa772005SRobert Watson * Find trail file by first part of the name in case it was renamed.
173aa772005SRobert Watson * First part of the trail file name never changes, but trail file
174aa772005SRobert Watson * can be renamed when hosts are disconnected from .not_terminated
175aa772005SRobert Watson * to .[0-9]{14} or to .crash_recovery.
176aa772005SRobert Watson */
177aa772005SRobert Watson static bool
trail_find(struct trail * trail)178aa772005SRobert Watson trail_find(struct trail *trail)
179aa772005SRobert Watson {
180aa772005SRobert Watson struct dirent *dp;
181aa772005SRobert Watson
182aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
183aa772005SRobert Watson PJDLOG_ASSERT(trail_is_not_terminated(trail->tr_filename));
184aa772005SRobert Watson
185aa772005SRobert Watson rewinddir(trail->tr_dirfp);
186aa772005SRobert Watson while ((dp = readdir(trail->tr_dirfp)) != NULL) {
187aa772005SRobert Watson if (strncmp(dp->d_name, trail->tr_filename, HALF_LEN + 1) == 0)
188aa772005SRobert Watson break;
189aa772005SRobert Watson }
190aa772005SRobert Watson if (dp == NULL)
191aa772005SRobert Watson return (false);
192aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(trail->tr_filename, dp->d_name,
193aa772005SRobert Watson sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
194aa772005SRobert Watson return (true);
195aa772005SRobert Watson }
196aa772005SRobert Watson
197aa772005SRobert Watson /*
198aa772005SRobert Watson * Open the given trail file and move pointer at the given offset, as this is
199aa772005SRobert Watson * where receiver finished the last time.
200aa772005SRobert Watson * If the file doesn't exist or the given offset is equal to the file size,
201aa772005SRobert Watson * move to the next trail file.
202aa772005SRobert Watson */
203aa772005SRobert Watson void
trail_start(struct trail * trail,const char * filename,off_t offset)204aa772005SRobert Watson trail_start(struct trail *trail, const char *filename, off_t offset)
205aa772005SRobert Watson {
206aa772005SRobert Watson struct stat sb;
207aa772005SRobert Watson int dfd, fd;
208aa772005SRobert Watson
209aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
210aa772005SRobert Watson
211aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(trail->tr_filename, filename,
212aa772005SRobert Watson sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
213aa772005SRobert Watson trail->tr_filefd = -1;
214aa772005SRobert Watson
215aa772005SRobert Watson if (trail->tr_filename[0] == '\0') {
216aa772005SRobert Watson PJDLOG_ASSERT(offset == 0);
217aa772005SRobert Watson trail_next(trail);
218aa772005SRobert Watson return;
219aa772005SRobert Watson }
220aa772005SRobert Watson
221aa772005SRobert Watson dfd = dirfd(trail->tr_dirfp);
222aa772005SRobert Watson PJDLOG_ASSERT(dfd >= 0);
223aa772005SRobert Watson again:
224aa772005SRobert Watson fd = openat(dfd, trail->tr_filename, O_RDONLY);
225aa772005SRobert Watson if (fd == -1) {
226aa772005SRobert Watson if (errno == ENOENT &&
227aa772005SRobert Watson trail_is_not_terminated(trail->tr_filename) &&
228aa772005SRobert Watson trail_find(trail)) {
229aa772005SRobert Watson /* File was renamed. Retry with new name. */
230aa772005SRobert Watson pjdlog_debug(1,
231aa772005SRobert Watson "Trail file was renamed since last connection to \"%s/%s\".",
232aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
233aa772005SRobert Watson goto again;
234aa772005SRobert Watson } else if (errno == ENOENT) {
235aa772005SRobert Watson /* File disappeared. */
236aa772005SRobert Watson pjdlog_debug(1, "File \"%s/%s\" doesn't exist.",
237aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
238aa772005SRobert Watson } else {
239aa772005SRobert Watson pjdlog_errno(LOG_ERR,
240aa772005SRobert Watson "Unable to open file \"%s/%s\", skipping",
241aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
242aa772005SRobert Watson }
243aa772005SRobert Watson trail_next(trail);
244aa772005SRobert Watson return;
245aa772005SRobert Watson }
246aa772005SRobert Watson if (fstat(fd, &sb) == -1) {
247aa772005SRobert Watson pjdlog_errno(LOG_ERR,
248aa772005SRobert Watson "Unable to stat file \"%s/%s\", skipping",
249aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
250aa772005SRobert Watson close(fd);
251aa772005SRobert Watson trail_next(trail);
252aa772005SRobert Watson return;
253aa772005SRobert Watson }
254aa772005SRobert Watson if (!S_ISREG(sb.st_mode)) {
255aa772005SRobert Watson pjdlog_warning("File \"%s/%s\" is not a regular file, skipping.",
256aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
257aa772005SRobert Watson close(fd);
258aa772005SRobert Watson trail_next(trail);
259aa772005SRobert Watson return;
260aa772005SRobert Watson }
261aa772005SRobert Watson /*
262aa772005SRobert Watson * We continue sending requested file if:
263aa772005SRobert Watson * 1. It is not fully sent yet, or
264aa772005SRobert Watson * 2. It is fully sent, but is not terminated, so new data can be
265aa772005SRobert Watson * appended still, or
266aa772005SRobert Watson * 3. It is fully sent but file name has changed.
267*4926792bSPawel Jakub Dawidek * There are two cases here:
268*4926792bSPawel Jakub Dawidek * 3a. Sender has crashed and the name has changed from
269*4926792bSPawel Jakub Dawidek * .not_terminated to .crash_recovery.
270*4926792bSPawel Jakub Dawidek * 3b. Sender was disconnected, no new data was added to the file,
271*4926792bSPawel Jakub Dawidek * but its name has changed from .not_terminated to terminated
272*4926792bSPawel Jakub Dawidek * name.
273aa772005SRobert Watson *
274aa772005SRobert Watson * Note that we are fine if our .not_terminated or .crash_recovery file
275aa772005SRobert Watson * is smaller than the one on the receiver side, as it is possible that
276aa772005SRobert Watson * more data was send to the receiver than was safely stored on disk.
277aa772005SRobert Watson * We accept .not_terminated only because auditdistd can start before
278aa772005SRobert Watson * auditd manage to rename it to .crash_recovery.
279aa772005SRobert Watson */
280aa772005SRobert Watson if (offset < sb.st_size ||
281aa772005SRobert Watson (offset >= sb.st_size &&
282aa772005SRobert Watson trail_is_not_terminated(trail->tr_filename)) ||
283aa772005SRobert Watson (offset >= sb.st_size && trail_is_not_terminated(filename) &&
284*4926792bSPawel Jakub Dawidek !trail_is_not_terminated(trail->tr_filename))) {
285aa772005SRobert Watson /* File was not fully send. Let's finish it. */
286aa772005SRobert Watson if (lseek(fd, offset, SEEK_SET) == -1) {
287aa772005SRobert Watson pjdlog_errno(LOG_ERR,
288aa772005SRobert Watson "Unable to move to offset %jd within file \"%s/%s\", skipping",
289aa772005SRobert Watson (intmax_t)offset, trail->tr_dirname,
290aa772005SRobert Watson trail->tr_filename);
291aa772005SRobert Watson close(fd);
292aa772005SRobert Watson trail_next(trail);
293aa772005SRobert Watson return;
294aa772005SRobert Watson }
295aa772005SRobert Watson if (!trail_is_crash_recovery(trail->tr_filename)) {
296aa772005SRobert Watson pjdlog_debug(1,
297aa772005SRobert Watson "Restarting file \"%s/%s\" at offset %jd.",
298aa772005SRobert Watson trail->tr_dirname, trail->tr_filename,
299aa772005SRobert Watson (intmax_t)offset);
300aa772005SRobert Watson }
301aa772005SRobert Watson trail->tr_filefd = fd;
302aa772005SRobert Watson return;
303aa772005SRobert Watson }
304aa772005SRobert Watson close(fd);
305aa772005SRobert Watson if (offset > sb.st_size) {
306aa772005SRobert Watson pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
307aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
308aa772005SRobert Watson } else {
309aa772005SRobert Watson pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
310aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
311aa772005SRobert Watson }
312aa772005SRobert Watson /* Entire file is already sent or it shirnked, we can remove it. */
313aa772005SRobert Watson if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
314aa772005SRobert Watson pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
315aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
316aa772005SRobert Watson }
317aa772005SRobert Watson trail_next(trail);
318aa772005SRobert Watson }
319aa772005SRobert Watson
320aa772005SRobert Watson /*
321aa772005SRobert Watson * Set next file in the trail->tr_dirname directory and open it for reading.
322aa772005SRobert Watson */
323aa772005SRobert Watson void
trail_next(struct trail * trail)324aa772005SRobert Watson trail_next(struct trail *trail)
325aa772005SRobert Watson {
326aa772005SRobert Watson char curfile[PATH_MAX];
327aa772005SRobert Watson struct dirent *dp;
328aa772005SRobert Watson int dfd;
329aa772005SRobert Watson
330aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
331aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_filefd == -1);
332aa772005SRobert Watson
333aa772005SRobert Watson again:
334aa772005SRobert Watson curfile[0] = '\0';
335aa772005SRobert Watson
336aa772005SRobert Watson rewinddir(trail->tr_dirfp);
337aa772005SRobert Watson while ((dp = readdir(trail->tr_dirfp)) != NULL) {
338aa772005SRobert Watson if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
339aa772005SRobert Watson continue;
340aa772005SRobert Watson if (dp->d_type == DT_UNKNOWN)
341aa772005SRobert Watson dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
342aa772005SRobert Watson /* We are only interested in regular files, skip the rest. */
343aa772005SRobert Watson if (dp->d_type != DT_REG) {
344aa772005SRobert Watson pjdlog_debug(1,
345aa772005SRobert Watson "File \"%s/%s\" is not a regular file, skipping.",
346aa772005SRobert Watson trail->tr_dirname, dp->d_name);
347aa772005SRobert Watson continue;
348aa772005SRobert Watson }
349aa772005SRobert Watson /* Skip all files "greater" than curfile. */
350aa772005SRobert Watson if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
351aa772005SRobert Watson continue;
352aa772005SRobert Watson /* Skip all files "smaller" than the current trail_filename. */
353aa772005SRobert Watson if (trail->tr_filename[0] != '\0' &&
354aa772005SRobert Watson strcmp(dp->d_name, trail->tr_filename) <= 0) {
355aa772005SRobert Watson continue;
356aa772005SRobert Watson }
357aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
358aa772005SRobert Watson sizeof(curfile));
359aa772005SRobert Watson }
360aa772005SRobert Watson if (curfile[0] == '\0') {
361aa772005SRobert Watson /*
362aa772005SRobert Watson * There are no new trail files, so we return.
363aa772005SRobert Watson * We don't clear trail_filename string, to know where to
364aa772005SRobert Watson * start when new file appears.
365aa772005SRobert Watson */
366aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_filefd == -1);
367aa772005SRobert Watson pjdlog_debug(1, "No new trail files.");
368aa772005SRobert Watson return;
369aa772005SRobert Watson }
370aa772005SRobert Watson dfd = dirfd(trail->tr_dirfp);
371aa772005SRobert Watson PJDLOG_ASSERT(dfd >= 0);
372ac67acf0SPawel Jakub Dawidek trail->tr_filefd = openat(dfd, curfile, O_RDONLY);
373aa772005SRobert Watson if (trail->tr_filefd == -1) {
374ac67acf0SPawel Jakub Dawidek if (errno == ENOENT && trail_is_not_terminated(curfile)) {
375ac67acf0SPawel Jakub Dawidek /*
376ac67acf0SPawel Jakub Dawidek * The .not_terminated file was most likely renamed.
377ac67acf0SPawel Jakub Dawidek * Keep trail->tr_filename as a starting point and
378ac67acf0SPawel Jakub Dawidek * search again.
379ac67acf0SPawel Jakub Dawidek */
380ac67acf0SPawel Jakub Dawidek pjdlog_debug(1,
381ac67acf0SPawel Jakub Dawidek "Unable to open \"%s/%s\", most likely renamed in the meantime, retrying.",
382ac67acf0SPawel Jakub Dawidek trail->tr_dirname, curfile);
383ac67acf0SPawel Jakub Dawidek } else {
384ac67acf0SPawel Jakub Dawidek /*
385ac67acf0SPawel Jakub Dawidek * We were unable to open the file, but not because of
386ac67acf0SPawel Jakub Dawidek * the above. This shouldn't happen, but it did.
387ac67acf0SPawel Jakub Dawidek * We don't know why it happen, so the best we can do
388ac67acf0SPawel Jakub Dawidek * is to just skip this file - this is why we copy the
389ac67acf0SPawel Jakub Dawidek * name, so we can start and the next entry.
390ac67acf0SPawel Jakub Dawidek */
391ac67acf0SPawel Jakub Dawidek PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
392ac67acf0SPawel Jakub Dawidek sizeof(trail->tr_filename)) <
393ac67acf0SPawel Jakub Dawidek sizeof(trail->tr_filename));
394aa772005SRobert Watson pjdlog_errno(LOG_ERR,
395aa772005SRobert Watson "Unable to open file \"%s/%s\", skipping",
396ac67acf0SPawel Jakub Dawidek trail->tr_dirname, curfile);
397ac67acf0SPawel Jakub Dawidek }
398aa772005SRobert Watson goto again;
399aa772005SRobert Watson }
400ac67acf0SPawel Jakub Dawidek PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
401ac67acf0SPawel Jakub Dawidek sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
402aa772005SRobert Watson pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
403aa772005SRobert Watson trail->tr_filename);
404aa772005SRobert Watson }
405aa772005SRobert Watson
406aa772005SRobert Watson /*
407aa772005SRobert Watson * Close current trial file.
408aa772005SRobert Watson */
409aa772005SRobert Watson void
trail_close(struct trail * trail)410aa772005SRobert Watson trail_close(struct trail *trail)
411aa772005SRobert Watson {
412aa772005SRobert Watson
413aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
414aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_filefd >= 0);
415aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
416aa772005SRobert Watson
417aa772005SRobert Watson PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
418aa772005SRobert Watson trail->tr_filefd = -1;
419aa772005SRobert Watson }
420aa772005SRobert Watson
421aa772005SRobert Watson /*
422aa772005SRobert Watson * Reset trail state. Used when connection is disconnected and we will
423aa772005SRobert Watson * need to start over after reconnect. Trail needs to be already closed.
424aa772005SRobert Watson */
425aa772005SRobert Watson void
trail_reset(struct trail * trail)426aa772005SRobert Watson trail_reset(struct trail *trail)
427aa772005SRobert Watson {
428aa772005SRobert Watson
429aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
430aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_filefd == -1);
431aa772005SRobert Watson
432aa772005SRobert Watson trail->tr_filename[0] = '\0';
433aa772005SRobert Watson }
434aa772005SRobert Watson
435aa772005SRobert Watson /*
436aa772005SRobert Watson * Unlink current trial file.
437aa772005SRobert Watson */
438aa772005SRobert Watson void
trail_unlink(struct trail * trail,const char * filename)439aa772005SRobert Watson trail_unlink(struct trail *trail, const char *filename)
440aa772005SRobert Watson {
441aa772005SRobert Watson int dfd;
442aa772005SRobert Watson
443aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
444aa772005SRobert Watson PJDLOG_ASSERT(filename != NULL);
445aa772005SRobert Watson PJDLOG_ASSERT(filename[0] != '\0');
446aa772005SRobert Watson
447aa772005SRobert Watson dfd = dirfd(trail->tr_dirfp);
448aa772005SRobert Watson PJDLOG_ASSERT(dfd >= 0);
449aa772005SRobert Watson
450aa772005SRobert Watson if (unlinkat(dfd, filename, 0) == -1) {
451aa772005SRobert Watson pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
452aa772005SRobert Watson trail->tr_dirname, filename);
453aa772005SRobert Watson } else {
454aa772005SRobert Watson pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
455aa772005SRobert Watson trail->tr_dirname, filename);
456aa772005SRobert Watson }
457aa772005SRobert Watson }
458aa772005SRobert Watson
459aa772005SRobert Watson /*
460aa772005SRobert Watson * Return true if we should switch to next trail file.
461aa772005SRobert Watson * We don't switch if our file name ends with ".not_terminated" and it
462aa772005SRobert Watson * exists (ie. wasn't renamed).
463aa772005SRobert Watson */
464aa772005SRobert Watson bool
trail_switch(struct trail * trail)465aa772005SRobert Watson trail_switch(struct trail *trail)
466aa772005SRobert Watson {
467aa772005SRobert Watson char filename[PATH_MAX];
468aa772005SRobert Watson int fd;
469aa772005SRobert Watson
470aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
471aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_filefd >= 0);
472aa772005SRobert Watson
473aa772005SRobert Watson if (!trail_is_not_terminated(trail->tr_filename))
474aa772005SRobert Watson return (true);
475aa772005SRobert Watson fd = dirfd(trail->tr_dirfp);
476aa772005SRobert Watson PJDLOG_ASSERT(fd >= 0);
477aa772005SRobert Watson if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
478aa772005SRobert Watson return (false);
479aa772005SRobert Watson if (errno != ENOENT) {
480aa772005SRobert Watson pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
481aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
482aa772005SRobert Watson }
483aa772005SRobert Watson strlcpy(filename, trail->tr_filename, sizeof(filename));
484aa772005SRobert Watson if (!trail_find(trail)) {
485aa772005SRobert Watson pjdlog_error("Trail file \"%s/%s\" disappeared.",
486aa772005SRobert Watson trail->tr_dirname, trail->tr_filename);
487aa772005SRobert Watson return (true);
488aa772005SRobert Watson }
489aa772005SRobert Watson pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
490aa772005SRobert Watson trail->tr_dirname, filename, trail->tr_dirname,
491aa772005SRobert Watson trail->tr_filename);
492aa772005SRobert Watson return (true);
493aa772005SRobert Watson }
494aa772005SRobert Watson
495aa772005SRobert Watson const char *
trail_filename(const struct trail * trail)496aa772005SRobert Watson trail_filename(const struct trail *trail)
497aa772005SRobert Watson {
498aa772005SRobert Watson
499aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
500aa772005SRobert Watson
501aa772005SRobert Watson return (trail->tr_filename);
502aa772005SRobert Watson }
503aa772005SRobert Watson
504aa772005SRobert Watson int
trail_filefd(const struct trail * trail)505aa772005SRobert Watson trail_filefd(const struct trail *trail)
506aa772005SRobert Watson {
507aa772005SRobert Watson
508aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
509aa772005SRobert Watson
510aa772005SRobert Watson return (trail->tr_filefd);
511aa772005SRobert Watson }
512aa772005SRobert Watson
513aa772005SRobert Watson int
trail_dirfd(const struct trail * trail)514aa772005SRobert Watson trail_dirfd(const struct trail *trail)
515aa772005SRobert Watson {
516aa772005SRobert Watson
517aa772005SRobert Watson PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
518aa772005SRobert Watson
519aa772005SRobert Watson return (dirfd(trail->tr_dirfp));
520aa772005SRobert Watson }
521aa772005SRobert Watson
522aa772005SRobert Watson /*
523aa772005SRobert Watson * Find the last file in the directory opened under dirfp.
524aa772005SRobert Watson */
525aa772005SRobert Watson void
trail_last(DIR * dirfp,char * filename,size_t filenamesize)526aa772005SRobert Watson trail_last(DIR *dirfp, char *filename, size_t filenamesize)
527aa772005SRobert Watson {
528aa772005SRobert Watson char curfile[PATH_MAX];
529aa772005SRobert Watson struct dirent *dp;
530aa772005SRobert Watson
531aa772005SRobert Watson PJDLOG_ASSERT(dirfp != NULL);
532aa772005SRobert Watson
533aa772005SRobert Watson curfile[0] = '\0';
534aa772005SRobert Watson
535aa772005SRobert Watson rewinddir(dirfp);
536aa772005SRobert Watson while ((dp = readdir(dirfp)) != NULL) {
537aa772005SRobert Watson if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
538aa772005SRobert Watson continue;
539aa772005SRobert Watson if (dp->d_type == DT_UNKNOWN)
540aa772005SRobert Watson dp->d_type = trail_type(dirfp, dp->d_name);
541aa772005SRobert Watson /* We are only interested in regular files, skip the rest. */
542aa772005SRobert Watson if (dp->d_type != DT_REG)
543aa772005SRobert Watson continue;
544aa772005SRobert Watson /* Skip all files "greater" than curfile. */
545aa772005SRobert Watson if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
546aa772005SRobert Watson continue;
547aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
548aa772005SRobert Watson sizeof(curfile));
549aa772005SRobert Watson }
550aa772005SRobert Watson if (curfile[0] == '\0') {
551aa772005SRobert Watson /*
552aa772005SRobert Watson * There are no trail files, so we return.
553aa772005SRobert Watson */
554aa772005SRobert Watson pjdlog_debug(1, "No trail files.");
555aa772005SRobert Watson bzero(filename, filenamesize);
556aa772005SRobert Watson return;
557aa772005SRobert Watson }
558aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
559aa772005SRobert Watson pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
560aa772005SRobert Watson }
561aa772005SRobert Watson
562aa772005SRobert Watson /*
563aa772005SRobert Watson * Check if the given file name is a valid audit trail file name.
564aa772005SRobert Watson * Possible names:
565aa772005SRobert Watson * 20120106132657.20120106132805
566aa772005SRobert Watson * 20120106132657.not_terminated
567aa772005SRobert Watson * 20120106132657.crash_recovery
568aa772005SRobert Watson * If two names are given, check if the first name can be renamed
569aa772005SRobert Watson * to the second name. When renaming, first part of the name has
570aa772005SRobert Watson * to be identical and only the following renames are valid:
571aa772005SRobert Watson * 20120106132657.not_terminated -> 20120106132657.20120106132805
572aa772005SRobert Watson * 20120106132657.not_terminated -> 20120106132657.crash_recovery
573aa772005SRobert Watson */
574aa772005SRobert Watson bool
trail_validate_name(const char * srcname,const char * dstname)575aa772005SRobert Watson trail_validate_name(const char *srcname, const char *dstname)
576aa772005SRobert Watson {
577aa772005SRobert Watson int i;
578aa772005SRobert Watson
579aa772005SRobert Watson PJDLOG_ASSERT(srcname != NULL);
580aa772005SRobert Watson
581aa772005SRobert Watson if (strlen(srcname) != 2 * HALF_LEN + 1)
582aa772005SRobert Watson return (false);
583aa772005SRobert Watson if (srcname[HALF_LEN] != '.')
584aa772005SRobert Watson return (false);
585aa772005SRobert Watson for (i = 0; i < HALF_LEN; i++) {
586aa772005SRobert Watson if (srcname[i] < '0' || srcname[i] > '9')
587aa772005SRobert Watson return (false);
588aa772005SRobert Watson }
589aa772005SRobert Watson for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
590aa772005SRobert Watson if (srcname[i] < '0' || srcname[i] > '9')
591aa772005SRobert Watson break;
592aa772005SRobert Watson }
593aa772005SRobert Watson if (i < 2 * HALF_LEN - 1 &&
594aa772005SRobert Watson strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
595aa772005SRobert Watson strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
596aa772005SRobert Watson return (false);
597aa772005SRobert Watson }
598aa772005SRobert Watson
599aa772005SRobert Watson if (dstname == NULL)
600aa772005SRobert Watson return (true);
601aa772005SRobert Watson
602aa772005SRobert Watson /* We tolarate if both names are identical. */
603aa772005SRobert Watson if (strcmp(srcname, dstname) == 0)
604aa772005SRobert Watson return (true);
605aa772005SRobert Watson
606aa772005SRobert Watson /* We can only rename not_terminated files. */
607aa772005SRobert Watson if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
608aa772005SRobert Watson return (false);
609aa772005SRobert Watson if (strlen(dstname) != 2 * HALF_LEN + 1)
610aa772005SRobert Watson return (false);
611aa772005SRobert Watson if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
612aa772005SRobert Watson return (false);
613aa772005SRobert Watson for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
614aa772005SRobert Watson if (dstname[i] < '0' || dstname[i] > '9')
615aa772005SRobert Watson break;
616aa772005SRobert Watson }
617aa772005SRobert Watson if (i < 2 * HALF_LEN - 1 &&
618aa772005SRobert Watson strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
619aa772005SRobert Watson return (false);
620aa772005SRobert Watson }
621aa772005SRobert Watson
622aa772005SRobert Watson return (true);
623aa772005SRobert Watson }
624aa772005SRobert Watson
625aa772005SRobert Watson int
trail_name_compare(const char * name0,const char * name1)626aa772005SRobert Watson trail_name_compare(const char *name0, const char *name1)
627aa772005SRobert Watson {
628aa772005SRobert Watson int ret;
629aa772005SRobert Watson
630aa772005SRobert Watson ret = strcmp(name0, name1);
631aa772005SRobert Watson if (ret == 0)
632aa772005SRobert Watson return (TRAIL_IDENTICAL);
633aa772005SRobert Watson if (strncmp(name0, name1, HALF_LEN + 1) == 0)
634aa772005SRobert Watson return (TRAIL_RENAMED);
635aa772005SRobert Watson return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);
636aa772005SRobert Watson }
637