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