You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
8.7 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* 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 <cups/dir.h>
#include <fnmatch.h>
#ifdef HAVE_REMOVEFILE
# include <removefile.h>
#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 */