Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/lib/isc/unix/file.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3
 * Copyright (C) 2000-2002  Internet Software Consortium.
4
 *
5
 * Permission to use, copy, modify, and/or distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
 * PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
/*
19
 * Portions Copyright (c) 1987, 1993
20
 *      The Regents of the University of California.  All rights reserved.
21
 *
22
 * Redistribution and use in source and binary forms, with or without
23
 * modification, are permitted provided that the following conditions
24
 * are met:
25
 * 1. Redistributions of source code must retain the above copyright
26
 *    notice, this list of conditions and the following disclaimer.
27
 * 2. Redistributions in binary form must reproduce the above copyright
28
 *    notice, this list of conditions and the following disclaimer in the
29
 *    documentation and/or other materials provided with the distribution.
30
 * 3. All advertising materials mentioning features or use of this software
31
 *    must display the following acknowledgement:
32
 *      This product includes software developed by the University of
33
 *      California, Berkeley and its contributors.
34
 * 4. Neither the name of the University nor the names of its contributors
35
 *    may be used to endorse or promote products derived from this software
36
 *    without specific prior written permission.
37
 *
38
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
42
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48
 * SUCH DAMAGE.
49
 */
50
51
/* $Id$ */
52
53
/*! \file */
54
55
#include <config.h>
56
57
#include <errno.h>
58
#include <fcntl.h>
59
#include <limits.h>
60
#include <stdlib.h>
61
#include <time.h>   /* Required for utimes on some platforms. */
62
#include <unistd.h>   /* Required for mkstemp on NetBSD. */
63
64
65
#include <sys/stat.h>
66
#include <sys/time.h>
67
68
#include <isc/dir.h>
69
#include <isc/file.h>
70
#include <isc/log.h>
71
#include <isc/mem.h>
72
#include <isc/random.h>
73
#include <isc/string.h>
74
#include <isc/time.h>
75
#include <isc/util.h>
76
77
#include "errno2result.h"
78
#include "ntp_stdlib.h"   /* NTP change for strlcpy, strlcat */
79
80
/*
81
 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
82
 * it might be good to provide a mechanism that allows for the results
83
 * of a previous stat() to be used again without having to do another stat,
84
 * such as perl's mechanism of using "_" in place of a file name to indicate
85
 * that the results of the last stat should be used.  But then you get into
86
 * annoying MP issues.   BTW, Win32 has stat().
87
 */
88
static isc_result_t
89
0
file_stats(const char *file, struct stat *stats) {
90
0
  isc_result_t result = ISC_R_SUCCESS;
91
92
0
  REQUIRE(file != NULL);
93
0
  REQUIRE(stats != NULL);
94
95
0
  if (stat(file, stats) != 0)
96
0
    result = isc__errno2result(errno);
97
98
0
  return (result);
99
0
}
100
101
isc_result_t
102
0
isc_file_getmodtime(const char *file, isc_time_t *itime) {
103
0
  isc_result_t result;
104
0
  struct stat stats;
105
106
0
  REQUIRE(file != NULL);
107
0
  REQUIRE(itime != NULL);
108
109
0
  result = file_stats(file, &stats);
110
111
0
  if (result == ISC_R_SUCCESS)
112
    /*
113
     * XXXDCL some operating systems provide nanoseconds, too,
114
     * such as BSD/OS via st_mtimespec.
115
     */
116
0
    isc_time_set(itime, stats.st_mtime, 0);
117
118
0
  return (result);
119
0
}
120
121
isc_result_t
122
0
isc_file_settime(const char *file, isc_time_t *itime) {
123
0
  struct timeval times[2];
124
125
0
  REQUIRE(file != NULL && itime != NULL);
126
127
  /*
128
   * tv_sec is at least a 32 bit quantity on all platforms we're
129
   * dealing with, but it is signed on most (all?) of them,
130
   * so we need to make sure the high bit isn't set.  This unfortunately
131
   * loses when either:
132
   *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
133
   *  and isc_time_seconds > LONG_MAX, or
134
   *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
135
   *      and isc_time_seconds has at least 33 significant bits.
136
   */
137
0
  times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(itime);
138
139
  /*
140
   * Here is the real check for the high bit being set.
141
   */
142
0
  if ((times[0].tv_sec &
143
0
       (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
144
0
    return (ISC_R_RANGE);
145
146
  /*
147
   * isc_time_nanoseconds guarantees a value that divided by 1000 will
148
   * fit into the minimum possible size tv_usec field.  Unfortunately,
149
   * we don't know what that type is so can't cast directly ... but
150
   * we can at least cast to signed so the IRIX compiler shuts up.
151
   */
152
0
  times[0].tv_usec = times[1].tv_usec =
153
0
    (isc_int32_t)(isc_time_nanoseconds(itime) / 1000);
154
155
0
  if (utimes(file, times) < 0)
156
0
    return (isc__errno2result(errno));
157
158
0
  return (ISC_R_SUCCESS);
159
0
}
160
161
#undef TEMPLATE
162
0
#define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
163
164
isc_result_t
165
0
isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
166
0
  return (isc_file_template(path, TEMPLATE, buf, buflen));
167
0
}
168
169
isc_result_t
170
isc_file_template(const char *path, const char *templet, char *buf,
171
0
      size_t buflen) {
172
0
  char *s;
173
174
0
  REQUIRE(path != NULL);
175
0
  REQUIRE(templet != NULL);
176
0
  REQUIRE(buf != NULL);
177
178
0
  s = strrchr(templet, '/');
179
0
  if (s != NULL)
180
0
    templet = s + 1;
181
182
0
  s = strrchr(path, '/');
183
184
0
  if (s != NULL) {
185
0
    if ((s - path + 1 + strlen(templet) + 1) > buflen)
186
0
      return (ISC_R_NOSPACE);
187
188
0
    strlcpy(buf, path, buflen);
189
0
    buf[s - path + 1] = '\0';
190
0
    strlcat(buf, templet, buflen);
191
0
  } else {
192
0
    if ((strlen(templet) + 1) > buflen)
193
0
      return (ISC_R_NOSPACE);
194
195
0
    strlcpy(buf, templet, buflen);
196
0
  }
197
198
0
  return (ISC_R_SUCCESS);
199
0
}
200
201
static char alphnum[] =
202
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
203
204
isc_result_t
205
0
isc_file_renameunique(const char *file, char *templet) {
206
0
  char *x;
207
0
  char *cp;
208
0
  isc_uint32_t which;
209
210
0
  REQUIRE(file != NULL);
211
0
  REQUIRE(templet != NULL);
212
213
0
  cp = templet;
214
0
  while (*cp != '\0')
215
0
    cp++;
216
0
  if (cp == templet)
217
0
    return (ISC_R_FAILURE);
218
219
0
  x = cp--;
220
0
  while (cp >= templet && *cp == 'X') {
221
0
    isc_random_get(&which);
222
0
    *cp = alphnum[which % (sizeof(alphnum) - 1)];
223
0
    x = cp--;
224
0
  }
225
0
  while (link(file, templet) == -1) {
226
0
    if (errno != EEXIST)
227
0
      return (isc__errno2result(errno));
228
0
    for (cp = x;;) {
229
0
      char *t;
230
0
      if (*cp == '\0')
231
0
        return (ISC_R_FAILURE);
232
0
      t = strchr(alphnum, *cp);
233
0
      if (t == NULL || *++t == '\0')
234
0
        *cp++ = alphnum[0];
235
0
      else {
236
0
        *cp = *t;
237
0
        break;
238
0
      }
239
0
    }
240
0
  }
241
0
  if (unlink(file) < 0)
242
0
    if (errno != ENOENT)
243
0
      return (isc__errno2result(errno));
244
0
  return (ISC_R_SUCCESS);
245
0
}
246
247
isc_result_t
248
0
isc_file_openunique(char *templet, FILE **fp) {
249
0
  int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
250
0
  return (isc_file_openuniquemode(templet, mode, fp));
251
0
}
252
253
isc_result_t
254
0
isc_file_openuniqueprivate(char *templet, FILE **fp) {
255
0
  int mode = S_IWUSR|S_IRUSR;
256
0
  return (isc_file_openuniquemode(templet, mode, fp));
257
0
}
258
259
isc_result_t
260
0
isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
261
0
  int fd;
262
0
  FILE *f;
263
0
  isc_result_t result = ISC_R_SUCCESS;
264
0
  char *x;
265
0
  char *cp;
266
0
  isc_uint32_t which;
267
268
0
  REQUIRE(templet != NULL);
269
0
  REQUIRE(fp != NULL && *fp == NULL);
270
271
0
  cp = templet;
272
0
  while (*cp != '\0')
273
0
    cp++;
274
0
  if (cp == templet)
275
0
    return (ISC_R_FAILURE);
276
277
0
  x = cp--;
278
0
  while (cp >= templet && *cp == 'X') {
279
0
    isc_random_get(&which);
280
0
    *cp = alphnum[which % (sizeof(alphnum) - 1)];
281
0
    x = cp--;
282
0
  }
283
284
285
0
  while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
286
0
    if (errno != EEXIST)
287
0
      return (isc__errno2result(errno));
288
0
    for (cp = x;;) {
289
0
      char *t;
290
0
      if (*cp == '\0')
291
0
        return (ISC_R_FAILURE);
292
0
      t = strchr(alphnum, *cp);
293
0
      if (t == NULL || *++t == '\0')
294
0
        *cp++ = alphnum[0];
295
0
      else {
296
0
        *cp = *t;
297
0
        break;
298
0
      }
299
0
    }
300
0
  }
301
0
  f = fdopen(fd, "w+");
302
0
  if (f == NULL) {
303
0
    result = isc__errno2result(errno);
304
0
    if (remove(templet) < 0) {
305
0
      isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
306
0
              ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
307
0
              "remove '%s': failed", templet);
308
0
    }
309
0
    (void)close(fd);
310
0
  } else
311
0
    *fp = f;
312
313
0
  return (result);
314
0
}
315
316
isc_result_t
317
0
isc_file_remove(const char *filename) {
318
0
  int r;
319
320
0
  REQUIRE(filename != NULL);
321
322
0
  r = unlink(filename);
323
0
  if (r == 0)
324
0
    return (ISC_R_SUCCESS);
325
0
  else
326
0
    return (isc__errno2result(errno));
327
0
}
328
329
isc_result_t
330
0
isc_file_rename(const char *oldname, const char *newname) {
331
0
  int r;
332
333
0
  REQUIRE(oldname != NULL);
334
0
  REQUIRE(newname != NULL);
335
336
0
  r = rename(oldname, newname);
337
0
  if (r == 0)
338
0
    return (ISC_R_SUCCESS);
339
0
  else
340
0
    return (isc__errno2result(errno));
341
0
}
342
343
isc_boolean_t
344
0
isc_file_exists(const char *pathname) {
345
0
  struct stat stats;
346
347
0
  REQUIRE(pathname != NULL);
348
349
0
  return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
350
0
}
351
352
isc_result_t
353
0
isc_file_isplainfile(const char *filename) {
354
  /*
355
   * This function returns success if filename is a plain file.
356
   */
357
0
  struct stat filestat;
358
0
  memset(&filestat,0,sizeof(struct stat));
359
360
0
  if ((stat(filename, &filestat)) == -1)
361
0
    return(isc__errno2result(errno));
362
363
0
  if(! S_ISREG(filestat.st_mode))
364
0
    return(ISC_R_INVALIDFILE);
365
366
0
  return(ISC_R_SUCCESS);
367
0
}
368
369
isc_boolean_t
370
0
isc_file_isabsolute(const char *filename) {
371
0
  REQUIRE(filename != NULL);
372
0
  return (ISC_TF(filename[0] == '/'));
373
0
}
374
375
isc_boolean_t
376
0
isc_file_iscurrentdir(const char *filename) {
377
0
  REQUIRE(filename != NULL);
378
0
  return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
379
0
}
380
381
isc_boolean_t
382
0
isc_file_ischdiridempotent(const char *filename) {
383
0
  REQUIRE(filename != NULL);
384
0
  if (isc_file_isabsolute(filename))
385
0
    return (ISC_TRUE);
386
0
  if (isc_file_iscurrentdir(filename))
387
0
    return (ISC_TRUE);
388
0
  return (ISC_FALSE);
389
0
}
390
391
const char *
392
0
isc_file_basename(const char *filename) {
393
0
  char *s;
394
395
0
  REQUIRE(filename != NULL);
396
397
0
  s = strrchr(filename, '/');
398
0
  if (s == NULL)
399
0
    return (filename);
400
401
0
  return (s + 1);
402
0
}
403
404
isc_result_t
405
0
isc_file_progname(const char *filename, char *buf, size_t buflen) {
406
0
  const char *base;
407
0
  size_t len;
408
409
0
  REQUIRE(filename != NULL);
410
0
  REQUIRE(buf != NULL);
411
412
0
  base = isc_file_basename(filename);
413
0
  len = strlen(base) + 1;
414
415
0
  if (len > buflen)
416
0
    return (ISC_R_NOSPACE);
417
0
  memcpy(buf, base, len);
418
419
0
  return (ISC_R_SUCCESS);
420
0
}
421
422
/*
423
 * Put the absolute name of the current directory into 'dirname', which is
424
 * a buffer of at least 'length' characters.  End the string with the
425
 * appropriate path separator, such that the final product could be
426
 * concatenated with a relative pathname to make a valid pathname string.
427
 */
428
static isc_result_t
429
0
dir_current(char *dirname, size_t length) {
430
0
  char *cwd;
431
0
  isc_result_t result = ISC_R_SUCCESS;
432
433
0
  REQUIRE(dirname != NULL);
434
0
  REQUIRE(length > 0U);
435
436
0
  cwd = getcwd(dirname, length);
437
438
0
  if (cwd == NULL) {
439
0
    if (errno == ERANGE)
440
0
      result = ISC_R_NOSPACE;
441
0
    else
442
0
      result = isc__errno2result(errno);
443
0
  } else {
444
0
    if (strlen(dirname) + 1 == length)
445
0
      result = ISC_R_NOSPACE;
446
0
    else if (dirname[1] != '\0')
447
0
      strlcat(dirname, "/", length);
448
0
  }
449
450
0
  return (result);
451
0
}
452
453
isc_result_t
454
0
isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
455
0
  isc_result_t result;
456
0
  result = dir_current(path, pathlen);
457
0
  if (result != ISC_R_SUCCESS)
458
0
    return (result);
459
0
  if (strlen(path) + strlen(filename) + 1 > pathlen)
460
0
    return (ISC_R_NOSPACE);
461
0
  strlcat(path, filename, pathlen);
462
0
  return (ISC_R_SUCCESS);
463
0
}
464
465
isc_result_t
466
0
isc_file_truncate(const char *filename, isc_offset_t size) {
467
0
  isc_result_t result = ISC_R_SUCCESS;
468
469
0
  if (truncate(filename, size) < 0)
470
0
    result = isc__errno2result(errno);
471
0
  return (result);
472
0
}
473
474
isc_result_t
475
0
isc_file_safecreate(const char *filename, FILE **fp) {
476
0
  isc_result_t result;
477
0
  int flags;
478
0
  struct stat sb;
479
0
  FILE *f;
480
0
  int fd;
481
482
0
  REQUIRE(filename != NULL);
483
0
  REQUIRE(fp != NULL && *fp == NULL);
484
485
0
  result = file_stats(filename, &sb);
486
0
  if (result == ISC_R_SUCCESS) {
487
0
    if ((sb.st_mode & S_IFREG) == 0)
488
0
      return (ISC_R_INVALIDFILE);
489
0
    flags = O_WRONLY | O_TRUNC;
490
0
  } else if (result == ISC_R_FILENOTFOUND) {
491
0
    flags = O_WRONLY | O_CREAT | O_EXCL;
492
0
  } else
493
0
    return (result);
494
495
0
  fd = open(filename, flags, S_IRUSR | S_IWUSR);
496
0
  if (fd == -1)
497
0
    return (isc__errno2result(errno));
498
499
0
  f = fdopen(fd, "w");
500
0
  if (f == NULL) {
501
0
    result = isc__errno2result(errno);
502
0
    close(fd);
503
0
    return (result);
504
0
  }
505
506
0
  *fp = f;
507
0
  return (ISC_R_SUCCESS);
508
0
}
509
510
isc_result_t
511
isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirnam, char **basenam)
512
0
{
513
0
  char *dir, *file, *slash;
514
515
0
  REQUIRE(path != NULL);
516
517
0
  slash = strrchr(path, '/');
518
519
0
  if (slash == path) {
520
0
    file = ++slash;
521
0
    dir = isc_mem_strdup(mctx, "/");
522
0
  } else if (slash != NULL) {
523
0
    file = ++slash;
524
0
    dir = isc_mem_allocate(mctx, slash - path);
525
0
    if (dir != NULL)
526
0
      strlcpy(dir, path, slash - path);
527
0
  } else {
528
0
    file = path;
529
0
    dir = isc_mem_strdup(mctx, ".");
530
0
  }
531
532
0
  if (dir == NULL)
533
0
    return (ISC_R_NOMEMORY);
534
535
0
  if (*file == '\0') {
536
0
    isc_mem_free(mctx, dir);
537
0
    return (ISC_R_INVALIDFILE);
538
0
  }
539
540
0
  *dirnam = dir;
541
0
  *basenam = file;
542
543
0
  return (ISC_R_SUCCESS);
544
0
}