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