1/*
2 * "$Id: file.c 11203 2013-07-26 21:32:33Z msweet $"
3 *
4 *   File functions for the CUPS scheduler.
5 *
6 *   Copyright 2007-2013 by Apple Inc.
7 *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 *   These coded instructions, statements, and computer programs are the
10 *   property of Apple Inc. and are protected by Federal copyright
11 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12 *   "LICENSE" which should have been included with this file.  If this
13 *   file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * Contents:
16 *
17 *   cupsdCleanFiles()		 - Clean out old files.
18 *   cupsdCloseCreatedConfFile() - Close a created configuration file and move
19 *				   into place.
20 *   cupsdClosePipe()		 - Close a pipe as necessary.
21 *   cupsdCreateConfFile()	 - Create a configuration file safely.
22 *   cupsdOpenConfFile()	 - Open a configuration file.
23 *   cupsdOpenPipe()		 - Create a pipe which is closed on exec.
24 *   cupsdRemoveFile()		 - Remove a file securely.
25 *   cupsdUnlinkOrRemoveFile()	 - Unlink or securely remove a file depending
26 *				   on the configuration.
27 *   overwrite_data()		 - Overwrite the data in a file.
28 */
29
30/*
31 * Include necessary headers...
32 */
33
34#include "cupsd.h"
35#include <cups/dir.h>
36#include <fnmatch.h>
37#ifdef HAVE_REMOVEFILE
38#  include <removefile.h>
39#else
40static int	overwrite_data(int fd, const char *buffer, int bufsize,
41		               int filesize);
42#endif /* HAVE_REMOVEFILE */
43
44
45/*
46 * 'cupsdCleanFiles()' - Clean out old files.
47 */
48
49void
50cupsdCleanFiles(const char *path,	/* I - Directory to clean */
51                const char *pattern)	/* I - Filename pattern or NULL */
52{
53  cups_dir_t	*dir;			/* Directory */
54  cups_dentry_t	*dent;			/* Directory entry */
55  char		filename[1024];		/* Filename */
56  int		status;			/* Status from unlink/rmdir */
57
58
59  cupsdLogMessage(CUPSD_LOG_DEBUG,
60                  "cupsdCleanFiles(path=\"%s\", pattern=\"%s\")", path,
61		  pattern ? pattern : "(null)");
62
63  if ((dir = cupsDirOpen(path)) == NULL)
64  {
65    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open directory \"%s\" - %s",
66		    path, strerror(errno));
67    return;
68  }
69
70  cupsdLogMessage(CUPSD_LOG_INFO, "Cleaning out old files in \"%s\".", path);
71
72  while ((dent = cupsDirRead(dir)) != NULL)
73  {
74    if (pattern && fnmatch(pattern, dent->filename, 0))
75      continue;
76
77    snprintf(filename, sizeof(filename), "%s/%s", path, dent->filename);
78
79    if (S_ISDIR(dent->fileinfo.st_mode))
80    {
81      cupsdCleanFiles(filename, pattern);
82
83      status = rmdir(filename);
84    }
85    else
86      status = cupsdUnlinkOrRemoveFile(filename);
87
88    if (status)
89      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove \"%s\" - %s", filename,
90		      strerror(errno));
91  }
92
93  cupsDirClose(dir);
94}
95
96
97/*
98 * 'cupsdCloseCreatedConfFile()' - Close a created configuration file and move
99 *                                 into place.
100 */
101
102int					/* O - 0 on success, -1 on error */
103cupsdCloseCreatedConfFile(
104    cups_file_t *fp,			/* I - File to close */
105    const char  *filename)		/* I - Filename */
106{
107  char	newfile[1024],			/* filename.N */
108	oldfile[1024];			/* filename.O */
109
110
111 /*
112  * Synchronize changes to disk if SyncOnClose is enabled.
113  */
114
115  if (SyncOnClose)
116  {
117    if (cupsFileFlush(fp))
118    {
119      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to write changes to \"%s\": %s",
120		      filename, strerror(errno));
121      cupsFileClose(fp);
122      return (-1);
123    }
124
125    if (fsync(cupsFileNumber(fp)))
126    {
127      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to sync changes to \"%s\": %s",
128		      filename, strerror(errno));
129      cupsFileClose(fp);
130      return (-1);
131    }
132  }
133
134 /*
135  * First close the file...
136  */
137
138  if (cupsFileClose(fp))
139    return (-1);
140
141 /*
142  * Then remove "filename.O", rename "filename" to "filename.O", and rename
143  * "filename.N" to "filename".
144  */
145
146  snprintf(newfile, sizeof(newfile), "%s.N", filename);
147  snprintf(oldfile, sizeof(oldfile), "%s.O", filename);
148
149  if ((cupsdUnlinkOrRemoveFile(oldfile) && errno != ENOENT) ||
150      (rename(filename, oldfile) && errno != ENOENT) ||
151      rename(newfile, filename))
152  {
153    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to finalize \"%s\": %s",
154                    filename, strerror(errno));
155    return (-1);
156  }
157
158  return (0);
159}
160
161
162/*
163 * 'cupsdClosePipe()' - Close a pipe as necessary.
164 */
165
166void
167cupsdClosePipe(int *fds)		/* I - Pipe file descriptors (2) */
168{
169 /*
170  * Close file descriptors as needed...
171  */
172
173  if (fds[0] >= 0)
174  {
175    close(fds[0]);
176    fds[0] = -1;
177  }
178
179  if (fds[1] >= 0)
180  {
181    close(fds[1]);
182    fds[1] = -1;
183  }
184}
185
186
187/*
188 * 'cupsdCreateConfFile()' - Create a configuration file safely.
189 */
190
191cups_file_t *				/* O - File pointer */
192cupsdCreateConfFile(
193    const char *filename,		/* I - Filename */
194    mode_t     mode)			/* I - Permissions */
195{
196  cups_file_t	*fp;			/* File pointer */
197  char		newfile[1024];		/* filename.N */
198
199
200  snprintf(newfile, sizeof(newfile), "%s.N", filename);
201  if ((fp = cupsFileOpen(newfile, "w")) == NULL)
202  {
203    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\": %s", newfile,
204		    strerror(errno));
205  }
206  else
207  {
208    if (!getuid() && fchown(cupsFileNumber(fp), getuid(), Group))
209      cupsdLogMessage(CUPSD_LOG_WARN, "Unable to change group for \"%s\": %s",
210		      newfile, strerror(errno));
211
212    if (fchmod(cupsFileNumber(fp), mode))
213      cupsdLogMessage(CUPSD_LOG_WARN,
214                      "Unable to change permissions for \"%s\": %s",
215		      newfile, strerror(errno));
216  }
217
218  return (fp);
219}
220
221
222/*
223 * 'cupsdOpenConfFile()' - Open a configuration file.
224 *
225 * This function looks for "filename.O" if "filename" does not exist and does
226 * a rename as needed.
227 */
228
229cups_file_t *				/* O - File pointer */
230cupsdOpenConfFile(const char *filename)	/* I - Filename */
231{
232  cups_file_t	*fp;			/* File pointer */
233
234
235  if ((fp = cupsFileOpen(filename, "r")) == NULL)
236  {
237    if (errno == ENOENT)
238    {
239     /*
240      * Try opening the backup file...
241      */
242
243      char	oldfile[1024];		/* filename.O */
244
245      snprintf(oldfile, sizeof(oldfile), "%s.O", filename);
246      fp = cupsFileOpen(oldfile, "r");
247    }
248    else
249      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\": %s", filename,
250		      strerror(errno));
251  }
252
253  return (fp);
254}
255
256
257/*
258 * 'cupsdOpenPipe()' - Create a pipe which is closed on exec.
259 */
260
261int					/* O - 0 on success, -1 on error */
262cupsdOpenPipe(int *fds)			/* O - Pipe file descriptors (2) */
263{
264 /*
265  * Create the pipe...
266  */
267
268  if (pipe(fds))
269  {
270    fds[0] = -1;
271    fds[1] = -1;
272
273    return (-1);
274  }
275
276 /*
277  * Set the "close on exec" flag on each end of the pipe...
278  */
279
280  if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
281  {
282    close(fds[0]);
283    close(fds[1]);
284
285    fds[0] = -1;
286    fds[1] = -1;
287
288    return (-1);
289  }
290
291  if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
292  {
293    close(fds[0]);
294    close(fds[1]);
295
296    fds[0] = -1;
297    fds[1] = -1;
298
299    return (-1);
300  }
301
302 /*
303  * Return 0 indicating success...
304  */
305
306  return (0);
307}
308
309
310/*
311 * 'cupsdRemoveFile()' - Remove a file securely.
312 */
313
314int					/* O - 0 on success, -1 on error */
315cupsdRemoveFile(const char *filename)	/* I - File to remove */
316{
317#ifdef HAVE_REMOVEFILE
318 /*
319  * See if the file exists...
320  */
321
322  if (access(filename, 0))
323    return (0);
324
325  cupsdLogMessage(CUPSD_LOG_DEBUG, "Securely removing \"%s\".", filename);
326
327 /*
328  * Remove the file...
329  */
330
331  return (removefile(filename, NULL, REMOVEFILE_SECURE_1_PASS));
332
333#else
334  int			fd;		/* File descriptor */
335  struct stat		info;		/* File information */
336  char			buffer[512];	/* Data buffer */
337  int			i;		/* Looping var */
338
339
340 /*
341  * See if the file exists...
342  */
343
344  if (access(filename, 0))
345    return (0);
346
347  cupsdLogMessage(CUPSD_LOG_DEBUG, "Securely removing \"%s\".", filename);
348
349 /*
350  * First open the file for writing in exclusive mode.
351  */
352
353  if ((fd = open(filename, O_WRONLY | O_EXCL)) < 0)
354    return (-1);
355
356 /*
357  * Delete the file now - it will still be around as long as the file is
358  * open...
359  */
360
361  if (unlink(filename))
362  {
363    close(fd);
364    return (-1);
365  }
366
367 /*
368  * Then get the file size...
369  */
370
371  if (fstat(fd, &info))
372  {
373    close(fd);
374    return (-1);
375  }
376
377 /*
378  * Overwrite the file with random data.
379  */
380
381  CUPS_SRAND(time(NULL));
382
383  for (i = 0; i < sizeof(buffer); i ++)
384    buffer[i] = CUPS_RAND();
385  if (overwrite_data(fd, buffer, sizeof(buffer), (int)info.st_size))
386  {
387    close(fd);
388    return (-1);
389  }
390
391 /*
392  * Close the file, which will lead to the actual deletion, and return...
393  */
394
395  return (close(fd));
396#endif /* HAVE_REMOVEFILE */
397}
398
399
400/*
401 * 'cupsdUnlinkOrRemoveFile()' - Unlink or securely remove a file depending
402 *                               on the configuration.
403 */
404
405int					/* O - 0 on success, -1 on error */
406cupsdUnlinkOrRemoveFile(
407    const char *filename)		/* I - Filename */
408{
409  if (Classification)
410    return (cupsdRemoveFile(filename));
411  else
412    return (unlink(filename));
413}
414
415
416#ifndef HAVE_REMOVEFILE
417/*
418 * 'overwrite_data()' - Overwrite the data in a file.
419 */
420
421static int				/* O - 0 on success, -1 on error */
422overwrite_data(int        fd,		/* I - File descriptor */
423               const char *buffer,	/* I - Buffer to write */
424	       int        bufsize,	/* I - Size of buffer */
425               int        filesize)	/* I - Size of file */
426{
427  int	bytes;				/* Bytes to write/written */
428
429
430 /*
431  * Start at the beginning of the file...
432  */
433
434  if (lseek(fd, 0, SEEK_SET) < 0)
435    return (-1);
436
437 /*
438  * Fill the file with the provided data...
439  */
440
441  while (filesize > 0)
442  {
443    if (filesize > bufsize)
444      bytes = bufsize;
445    else
446      bytes = filesize;
447
448    if ((bytes = write(fd, buffer, bytes)) < 0)
449      return (-1);
450
451    filesize -= bytes;
452  }
453
454 /*
455  * Force the changes to disk...
456  */
457
458  return (fsync(fd));
459}
460#endif /* HAVE_REMOVEFILE */
461
462
463/*
464 * End of "$Id: file.c 11203 2013-07-26 21:32:33Z msweet $".
465 */
466