/* * File functions for the CUPS scheduler. * * Copyright © 2007-2014 by Apple Inc. * Copyright © 1997-2007 by Easy Software Products, all rights reserved. * * Licensed under Apache License v2.0. See the file "LICENSE" for more * information. */ /* * Include necessary headers... */ #include "cupsd.h" #include #include #ifdef HAVE_REMOVEFILE # include #else static int overwrite_data(int fd, const char *buffer, int bufsize, int filesize); #endif /* HAVE_REMOVEFILE */ /* * 'cupsdCleanFiles()' - Clean out old files. */ void cupsdCleanFiles(const char *path, /* I - Directory to clean */ const char *pattern) /* I - Filename pattern or NULL */ { cups_dir_t *dir; /* Directory */ cups_dentry_t *dent; /* Directory entry */ char filename[1024]; /* Filename */ int status; /* Status from unlink/rmdir */ cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCleanFiles(path=\"%s\", pattern=\"%s\")", path, pattern ? pattern : "(null)"); if ((dir = cupsDirOpen(path)) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open directory \"%s\" - %s", path, strerror(errno)); return; } cupsdLogMessage(CUPSD_LOG_INFO, "Cleaning out old files in \"%s\".", path); while ((dent = cupsDirRead(dir)) != NULL) { if (pattern && fnmatch(pattern, dent->filename, 0)) continue; snprintf(filename, sizeof(filename), "%s/%s", path, dent->filename); if (S_ISDIR(dent->fileinfo.st_mode)) { cupsdCleanFiles(filename, pattern); status = rmdir(filename); } else status = cupsdUnlinkOrRemoveFile(filename); if (status) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove \"%s\" - %s", filename, strerror(errno)); } cupsDirClose(dir); } /* * 'cupsdCloseCreatedConfFile()' - Close a created configuration file and move * into place. */ int /* O - 0 on success, -1 on error */ cupsdCloseCreatedConfFile( cups_file_t *fp, /* I - File to close */ const char *filename) /* I - Filename */ { char newfile[1024], /* filename.N */ oldfile[1024]; /* filename.O */ /* * Synchronize changes to disk if SyncOnClose is enabled. */ if (SyncOnClose) { if (cupsFileFlush(fp)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to write changes to \"%s\": %s", filename, strerror(errno)); cupsFileClose(fp); return (-1); } if (fsync(cupsFileNumber(fp))) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to sync changes to \"%s\": %s", filename, strerror(errno)); cupsFileClose(fp); return (-1); } } /* * First close the file... */ if (cupsFileClose(fp)) return (-1); /* * Then remove "filename.O", rename "filename" to "filename.O", and rename * "filename.N" to "filename". */ snprintf(newfile, sizeof(newfile), "%s.N", filename); snprintf(oldfile, sizeof(oldfile), "%s.O", filename); if ((cupsdUnlinkOrRemoveFile(oldfile) && errno != ENOENT) || (rename(filename, oldfile) && errno != ENOENT) || rename(newfile, filename)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to finalize \"%s\": %s", filename, strerror(errno)); return (-1); } return (0); } /* * 'cupsdClosePipe()' - Close a pipe as necessary. */ void cupsdClosePipe(int *fds) /* I - Pipe file descriptors (2) */ { /* * Close file descriptors as needed... */ if (fds[0] >= 0) { close(fds[0]); fds[0] = -1; } if (fds[1] >= 0) { close(fds[1]); fds[1] = -1; } } /* * 'cupsdCreateConfFile()' - Create a configuration file safely. */ cups_file_t * /* O - File pointer */ cupsdCreateConfFile( const char *filename, /* I - Filename */ mode_t mode) /* I - Permissions */ { cups_file_t *fp; /* File pointer */ char newfile[1024]; /* filename.N */ snprintf(newfile, sizeof(newfile), "%s.N", filename); if ((fp = cupsFileOpen(newfile, "w")) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\": %s", newfile, strerror(errno)); } else { if (!getuid() && fchown(cupsFileNumber(fp), getuid(), Group)) cupsdLogMessage(CUPSD_LOG_WARN, "Unable to change group for \"%s\": %s", newfile, strerror(errno)); if (fchmod(cupsFileNumber(fp), mode)) cupsdLogMessage(CUPSD_LOG_WARN, "Unable to change permissions for \"%s\": %s", newfile, strerror(errno)); } return (fp); } /* * 'cupsdOpenConfFile()' - Open a configuration file. * * This function looks for "filename.O" if "filename" does not exist and does * a rename as needed. */ cups_file_t * /* O - File pointer */ cupsdOpenConfFile(const char *filename) /* I - Filename */ { cups_file_t *fp; /* File pointer */ if ((fp = cupsFileOpen(filename, "r")) == NULL) { if (errno == ENOENT) { /* * Try opening the backup file... */ char oldfile[1024]; /* filename.O */ snprintf(oldfile, sizeof(oldfile), "%s.O", filename); fp = cupsFileOpen(oldfile, "r"); } else cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\": %s", filename, strerror(errno)); } return (fp); } /* * 'cupsdOpenPipe()' - Create a pipe which is closed on exec. */ int /* O - 0 on success, -1 on error */ cupsdOpenPipe(int *fds) /* O - Pipe file descriptors (2) */ { /* * Create the pipe... */ if (pipe(fds)) { fds[0] = -1; fds[1] = -1; return (-1); } /* * Set the "close on exec" flag on each end of the pipe... */ if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC)) { close(fds[0]); close(fds[1]); fds[0] = -1; fds[1] = -1; return (-1); } if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC)) { close(fds[0]); close(fds[1]); fds[0] = -1; fds[1] = -1; return (-1); } /* * Return 0 indicating success... */ return (0); } /* * 'cupsdRemoveFile()' - Remove a file securely. */ int /* O - 0 on success, -1 on error */ cupsdRemoveFile(const char *filename) /* I - File to remove */ { #ifdef HAVE_REMOVEFILE /* * See if the file exists... */ if (access(filename, 0)) return (0); cupsdLogMessage(CUPSD_LOG_DEBUG, "Securely removing \"%s\".", filename); /* * Remove the file... */ return (removefile(filename, NULL, REMOVEFILE_SECURE_1_PASS)); #else int fd; /* File descriptor */ struct stat info; /* File information */ char buffer[512]; /* Data buffer */ int i; /* Looping var */ /* * See if the file exists... */ if (access(filename, 0)) return (0); cupsdLogMessage(CUPSD_LOG_DEBUG, "Securely removing \"%s\".", filename); /* * First open the file for writing in exclusive mode. */ if ((fd = open(filename, O_WRONLY | O_EXCL)) < 0) return (-1); /* * Delete the file now - it will still be around as long as the file is * open... */ if (unlink(filename)) { close(fd); return (-1); } /* * Then get the file size... */ if (fstat(fd, &info)) { close(fd); return (-1); } /* * Overwrite the file with random data. */ CUPS_SRAND(time(NULL)); for (i = 0; i < sizeof(buffer); i ++) buffer[i] = CUPS_RAND(); if (overwrite_data(fd, buffer, sizeof(buffer), (int)info.st_size)) { close(fd); return (-1); } /* * Close the file, which will lead to the actual deletion, and return... */ return (close(fd)); #endif /* HAVE_REMOVEFILE */ } /* * 'cupsdUnlinkOrRemoveFile()' - Unlink or securely remove a file depending * on the configuration. */ int /* O - 0 on success, -1 on error */ cupsdUnlinkOrRemoveFile( const char *filename) /* I - Filename */ { if (Classification) return (cupsdRemoveFile(filename)); else return (unlink(filename)); } #ifndef HAVE_REMOVEFILE /* * 'overwrite_data()' - Overwrite the data in a file. */ static int /* O - 0 on success, -1 on error */ overwrite_data(int fd, /* I - File descriptor */ const char *buffer, /* I - Buffer to write */ int bufsize, /* I - Size of buffer */ int filesize) /* I - Size of file */ { int bytes; /* Bytes to write/written */ /* * Start at the beginning of the file... */ if (lseek(fd, 0, SEEK_SET) < 0) return (-1); /* * Fill the file with the provided data... */ while (filesize > 0) { if (filesize > bufsize) bytes = bufsize; else bytes = filesize; if ((bytes = write(fd, buffer, (size_t)bytes)) < 0) return (-1); filesize -= bytes; } /* * Force the changes to disk... */ return (fsync(fd)); } #endif /* HAVE_REMOVEFILE */