• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 14526507026

17 Apr 2025 11:25PM UTC coverage: 93.03% (+0.4%) from 92.667%
14526507026

push

github

51358 of 55206 relevant lines covered (93.03%)

234.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.14
/src/fsio.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5
 * Copyright (c) 2001-2025 The ProFTPD Project
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20
 *
21
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22
 * and other respective copyright holders give permission to link this program
23
 * with OpenSSL, and distribute the resulting executable, without including
24
 * the source code for OpenSSL in the source distribution.
25
 */
26

27
/* ProFTPD virtual/modular file-system support */
28

29
#include "error.h"
30
#include "conf.h"
31
#include "privs.h"
32

33
#ifdef HAVE_SYS_STATVFS_H
34
# include <sys/statvfs.h>
35
#endif
36

37
#ifdef HAVE_SYS_VFS_H
38
# include <sys/vfs.h>
39
#endif
40

41
#ifdef HAVE_SYS_PARAM_H
42
# include <sys/param.h>
43
#endif
44

45
#ifdef HAVE_SYS_MOUNT_H
46
# include <sys/mount.h>
47
#endif
48

49
#ifdef AIX3
50
# include <sys/statfs.h>
51
#endif
52

53
#ifdef HAVE_ACL_LIBACL_H
54
# include <acl/libacl.h>
55
#endif
56

57
/* We will reset timers in the progress callback every Nth iteration of the
58
 * callback when copying a file.
59
 */
60
static size_t copy_iter_count = 0;
61

62
#ifndef COPY_PROGRESS_NTH_ITER
63
# define COPY_PROGRESS_NTH_ITER       50000
64
#endif
65

66
/* For determining whether a file is on an NFS filesystem.  Note that
67
 * this value is Linux specific.  See Bug#3874 for details.
68
 */
69
#ifndef NFS_SUPER_MAGIC
70
# define NFS_SUPER_MAGIC        0x6969
71
#endif
72

73
typedef struct fsopendir fsopendir_t;
74

75
struct fsopendir {
76
  fsopendir_t *next,*prev;
77

78
  /* pool for this object's use */
79
  pool *pool;
80

81
  pr_fs_t *fsdir;
82
  DIR *dir;
83
};
84

85
static pr_fs_t *root_fs = NULL, *fs_cwd = NULL;
86
static array_header *fs_map = NULL;
87

88
static fsopendir_t *fsopendir_list;
89

90
static void *fs_cache_dir = NULL;
91
static pr_fs_t *fs_cache_fsdir = NULL;
92

93
/* Internal flag set whenever a new pr_fs_t has been added or removed, and
94
 * cleared once the fs_map has been scanned
95
 */
96
static unsigned char chk_fs_map = FALSE;
97

98
/* Virtual working directory */
99
static char vwd[PR_TUNABLE_PATH_MAX + 1] = "/";
100

101
static char cwd[PR_TUNABLE_PATH_MAX + 1] = "/";
102
static size_t cwd_len = 1;
103

104
static int fsio_guard_chroot = FALSE;
105
static unsigned long fsio_opts = 0UL;
106

107
/* Runtime enabling/disabling of mkdtemp(3) use. */
108
#ifdef HAVE_MKDTEMP
109
static int fsio_use_mkdtemp = TRUE;
110
#else
111
static int fsio_use_mkdtemp = FALSE;
112
#endif /* HAVE_MKDTEMP */
113

114
/* Runtime enabling/disabling of encoding of paths. */
115
static int use_encoding = TRUE;
116

117
static const char *trace_channel = "fsio";
118

119
/* Guard against attacks like "Roaring Beast" when we are chrooted.  See:
120
 *
121
 *  https://auscert.org.au/15286
122
 *  https://auscert.org.au/15526
123
 *
124
 * Currently, we guard the /etc and /lib directories.
125
 */
126
static int chroot_allow_path(const char *path) {
18✔
127
  size_t path_len;
18✔
128
  int res = 0;
18✔
129

130
  /* Note: we expect to get (and DO get) the absolute path here.  Should that
131
   * ever not be the case, this check will not work.
132
   */
133

134
  path_len = strlen(path);
18✔
135
  if (path_len < 4) {
18✔
136
    /* Path is not long enough to include one of the guarded directories. */
137
    return 0;
138
  }
139

140
  if (path_len == 4) {
17✔
141
    if (strcmp(path, "/etc") == 0 ||
2✔
142
        strcmp(path, "/lib") == 0) {
1✔
143
      res = -1;
144
    }
145

146
  } else {
147
    if (strncmp(path, "/etc/", 5) == 0 ||
15✔
148
        strncmp(path, "/lib/", 5) == 0) {
2✔
149
      res = -1;
150
    }
151
  }
152

153
  if (res < 0) {
154
    pr_trace_msg(trace_channel, 1, "rejecting path '%s' within chroot '%s'",
15✔
155
      path, session.chroot_path);
156
    pr_log_debug(DEBUG2,
15✔
157
      "WARNING: attempt to use sensitive path '%s' within chroot '%s', "
158
      "rejecting", path, session.chroot_path);
159

160
    errno = EACCES;
15✔
161
  }
162

163
  return res;
164
}
165

166
/* Builtin/default "progress" callback for long-running file copies. */
167
static void copy_progress_cb(int nwritten) {
2✔
168
  int res;
2✔
169

170
  (void) nwritten;
2✔
171

172
  copy_iter_count++;
2✔
173
  if ((copy_iter_count % COPY_PROGRESS_NTH_ITER) != 0) {
2✔
174
    return;
175
  }
176

177
  /* Reset some of the Timeouts which might interfere, i.e. TimeoutIdle and
178
   * TimeoutNoDataTransfer.
179
   */
180

181
  res = pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);
×
182
  if (res < 0) {
×
183
    pr_trace_msg(trace_channel, 14, "error resetting TimeoutIdle timer: %s",
×
184
      strerror(errno));
×
185
  }
186

187
  res = pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE);
×
188
  if (res < 0) {
×
189
    pr_trace_msg(trace_channel, 14,
×
190
      "error resetting TimeoutNoTransfer timer: %s", strerror(errno));
×
191
  }
192

193
  res = pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
×
194
  if (res < 0) {
×
195
    pr_trace_msg(trace_channel, 14,
×
196
      "error resetting TimeoutStalled timer: %s", strerror(errno));
×
197
  }
198
}
199

200
/* The following static functions are simply wrappers for system functions
201
 */
202

203
static int sys_stat(pr_fs_t *fs, const char *path, struct stat *sbuf) {
40✔
204
  return stat(path, sbuf);
40✔
205
}
206

207
static int sys_fstat(pr_fh_t *fh, int fd, struct stat *sbuf) {
36✔
208
  return fstat(fd, sbuf);
36✔
209
}
210

211
static int sys_lstat(pr_fs_t *fs, const char *path, struct stat *sbuf) {
1,498✔
212
  return lstat(path, sbuf);
1,498✔
213
}
214

215
static int sys_rename(pr_fs_t *fs, const char *rnfm, const char *rnto) {
8✔
216
  int res;
8✔
217

218
  if (fsio_guard_chroot) {
8✔
219
    res = chroot_allow_path(rnfm);
2✔
220
    if (res < 0) {
2✔
221
      return -1;
222
    }
223

224
    res = chroot_allow_path(rnto);
1✔
225
    if (res < 0) {
1✔
226
      return -1;
227
    }
228
  }
229

230
  res = rename(rnfm, rnto);
6✔
231
  return res;
6✔
232
}
233

234
static int sys_unlink(pr_fs_t *fs, const char *path) {
75✔
235
  int res;
75✔
236

237
  if (fsio_guard_chroot) {
75✔
238
    res = chroot_allow_path(path);
1✔
239
    if (res < 0) {
1✔
240
      return -1;
241
    }
242
  }
243

244
  res = unlink(path);
74✔
245
  return res;
74✔
246
}
247

248
static int sys_open(pr_fh_t *fh, const char *path, int flags) {
82✔
249
  int res;
82✔
250

251
#ifdef O_BINARY
252
  /* On Cygwin systems, we need the open(2) equivalent of fopen(3)'s "b"
253
   * option.  Cygwin defines an O_BINARY flag for this purpose.
254
   */
255
  flags |= O_BINARY;
256
#endif
257

258
  if (fsio_guard_chroot) {
82✔
259
    /* If we are creating (or truncating) a file, then we need to check.
260
     * Note: should O_RDWR be added to this list?
261
     */
262
    if (flags & (O_APPEND|O_CREAT|O_TRUNC|O_WRONLY)) {
5✔
263
      res = chroot_allow_path(path);
5✔
264
      if (res < 0) {
5✔
265
        return -1;
266
      }
267
    }
268
  }
269

270
  res = open(path, flags, PR_OPEN_MODE);
79✔
271
  return res;
272
}
273

274
static int sys_close(pr_fh_t *fh, int fd) {
67✔
275
  return close(fd);
67✔
276
}
277

278
static ssize_t sys_pread(pr_fh_t *fh, int fd, void *buf, size_t sz,
1✔
279
    off_t offset) {
280
#if defined(HAVE_PREAD)
281
  return pread(fd, buf, sz, offset);
1✔
282
#else
283
  /* XXX Provide an implementation using lseek+read+lseek */
284
  errno = ENOSYS;
285
  return -1;
286
#endif /* HAVE_PREAD */
287
}
288

289
static int sys_read(pr_fh_t *fh, int fd, char *buf, size_t size) {
35✔
290
  return read(fd, buf, size);
35✔
291
}
292

293
static ssize_t sys_pwrite(pr_fh_t *fh, int fd, const void *buf, size_t sz,
2✔
294
    off_t offset) {
295
#if defined(HAVE_PWRITE)
296
  return pwrite(fd, buf, sz, offset);
2✔
297
#else
298
  /* XXX Provide an implementation using lseek+write+lseek */
299
  errno = ENOSYS;
300
  return -1;
301
#endif /* HAVE_PWRITE */
302
}
303

304
static int sys_write(pr_fh_t *fh, int fd, const char *buf, size_t size) {
18✔
305
  return write(fd, buf, size);
18✔
306
}
307

308
static off_t sys_lseek(pr_fh_t *fh, int fd, off_t offset, int whence) {
3✔
309
  return lseek(fd, offset, whence);
3✔
310
}
311

312
static int sys_link(pr_fs_t *fs, const char *target_path,
4✔
313
    const char *link_path) {
314
  int res;
4✔
315

316
  if (fsio_guard_chroot) {
4✔
317
    res = chroot_allow_path(link_path);
1✔
318
    if (res < 0) {
1✔
319
      return -1;
320
    }
321
  }
322

323
  res = link(target_path, link_path);
3✔
324
  return res;
3✔
325
}
326

327
static int sys_symlink(pr_fs_t *fs, const char *target_path,
9✔
328
    const char *link_path) {
329
  int res;
9✔
330

331
  if (fsio_guard_chroot) {
9✔
332
    res = chroot_allow_path(link_path);
1✔
333
    if (res < 0) {
1✔
334
      return -1;
335
    }
336
  }
337

338
  res = symlink(target_path, link_path);
8✔
339
  return res;
8✔
340
}
341

342
static int sys_readlink(pr_fs_t *fs, const char *path, char *buf,
28✔
343
    size_t buflen) {
344
  return readlink(path, buf, buflen);
28✔
345
}
346

347
static int sys_ftruncate(pr_fh_t *fh, int fd, off_t len) {
2✔
348
  return ftruncate(fd, len);
2✔
349
}
350

351
static int sys_truncate(pr_fs_t *fs, const char *path, off_t len) {
6✔
352
  int res;
6✔
353

354
  if (fsio_guard_chroot) {
6✔
355
    res = chroot_allow_path(path);
1✔
356
    if (res < 0) {
1✔
357
      return -1;
358
    }
359
  }
360

361
  res = truncate(path, len);
5✔
362
  return res;
5✔
363
}
364

365
static int sys_chmod(pr_fs_t *fs, const char *path, mode_t mode) {
7✔
366
  int res;
7✔
367

368
  if (fsio_guard_chroot) {
7✔
369
    res = chroot_allow_path(path);
1✔
370
    if (res < 0) {
1✔
371
      return -1;
372
    }
373
  }
374

375
  res = chmod(path, mode);
6✔
376
  return res;
6✔
377
}
378

379
static int sys_fchmod(pr_fh_t *fh, int fd, mode_t mode) {
1✔
380
  return fchmod(fd, mode);
1✔
381
}
382

383
static int sys_chown(pr_fs_t *fs, const char *path, uid_t uid, gid_t gid) {
7✔
384
  int res;
7✔
385

386
  if (fsio_guard_chroot) {
7✔
387
    res = chroot_allow_path(path);
1✔
388
    if (res < 0) {
1✔
389
      return -1;
390
    }
391
  }
392

393
  res = chown(path, uid, gid);
6✔
394
  return res;
6✔
395
}
396

397
static int sys_fchown(pr_fh_t *fh, int fd, uid_t uid, gid_t gid) {
1✔
398
  return fchown(fd, uid, gid);
1✔
399
}
400

401
static int sys_lchown(pr_fs_t *fs, const char *path, uid_t uid, gid_t gid) {
9✔
402
  int res;
9✔
403

404
  if (fsio_guard_chroot) {
9✔
405
    res = chroot_allow_path(path);
1✔
406
    if (res < 0) {
1✔
407
      return -1;
408
    }
409
  }
410

411
  res = lchown(path, uid, gid);
8✔
412
  return res;
8✔
413
}
414

415
/* We provide our own equivalent of access(2) here, rather than using
416
 * access(2) directly, because access(2) uses the real IDs, rather than
417
 * the effective IDs, of the process.
418
 */
419
static int sys_access(pr_fs_t *fs, const char *path, int mode, uid_t uid,
19✔
420
    gid_t gid, array_header *suppl_gids) {
421
  struct stat st;
19✔
422

423
  if (pr_fsio_stat(path, &st) < 0) {
19✔
424
    return -1;
425
  }
426

427
  return pr_fs_have_access(&st, mode, uid, gid, suppl_gids);
18✔
428
}
429

430
static int sys_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
4✔
431
    array_header *suppl_gids) {
432
  return sys_access(fh->fh_fs, fh->fh_path, mode, uid, gid, suppl_gids);
4✔
433
}
434

435
static int sys_utimes(pr_fs_t *fs, const char *path, struct timeval *tvs) {
4✔
436
  int res;
4✔
437

438
  if (fsio_guard_chroot) {
4✔
439
    res = chroot_allow_path(path);
1✔
440
    if (res < 0) {
1✔
441
      return -1;
442
    }
443
  }
444

445
  res = utimes(path, tvs);
3✔
446
  return res;
3✔
447
}
448

449
static int sys_futimes(pr_fh_t *fh, int fd, struct timeval *tvs) {
1✔
450
#ifdef HAVE_FUTIMES
451
  int res;
1✔
452

453
  /* Check for an ENOSYS errno; if so, fallback to using sys_utimes.  Some
454
   * platforms will provide a futimes(2) stub which does not actually do
455
   * anything.
456
   */
457
  res = futimes(fd, tvs);
1✔
458
  if (res < 0 &&
1✔
459
      errno == ENOSYS) {
×
460
    return sys_utimes(fh->fh_fs, fh->fh_path, tvs);
×
461
  }
462

463
  return res;
464
#else
465
  return sys_utimes(fh->fh_fs, fh->fh_path, tvs);
466
#endif
467
}
468

469
static int sys_fsync(pr_fh_t *fh, int fd) {
3✔
470
  int res;
3✔
471

472
#ifdef HAVE_FSYNC
473
  res = fsync(fd);
3✔
474
#else
475
  errno = ENOSYS;
476
  res = -1;
477
#endif /* HAVE_FSYNC */
478

479
  return res;
3✔
480
}
481

482
static const char *sys_realpath(pr_fs_t *fs, pool *p, const char *path) {
26✔
483
  (void) fs;
26✔
484

485
  /* The default implementation does NOT use the realpath(3) function, and
486
   * instead is a pass-through.
487
   */
488
  return pstrdup(p, path);
26✔
489
}
490

491
static ssize_t sys_getxattr(pool *p, pr_fs_t *fs, const char *path,
1✔
492
    const char *name, void *val, size_t valsz) {
493
  ssize_t res = -1;
1✔
494

495
  (void) p;
1✔
496

497
#if defined(PR_USE_XATTR)
498
# if defined(HAVE_SYS_EXTATTR_H)
499
  res = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
500
# elif defined(HAVE_SYS_XATTR_H)
501
#  if defined(XATTR_NOFOLLOW)
502
  res = getxattr(path, name, val, valsz, 0, 0);
503
#  else
504
  res = getxattr(path, name, val, valsz);
1✔
505
#  endif /* XATTR_NOFOLLOW */
506
# else
507
  errno = NOSYS;
508
  res = -1;
509
# endif /* HAVE_SYS_XATTR_H */
510
#else
511
  (void) fs;
512
  (void) path;
513
  (void) name;
514
  (void) val;
515
  (void) valsz;
516

517
  errno = ENOSYS;
518
  res = -1;
519
#endif /* PR_USE_XATTR */
520

521
  return res;
1✔
522
}
523

524
static ssize_t sys_lgetxattr(pool *p, pr_fs_t *fs, const char *path,
1✔
525
    const char *name, void *val, size_t valsz) {
526
  ssize_t res = -1;
1✔
527

528
  (void) p;
1✔
529

530
#if defined(PR_USE_XATTR)
531
# if defined(HAVE_SYS_EXTATTR_H)
532
#  if defined(HAVE_EXTATTR_GET_LINK)
533
  res = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
534
#  else
535
  res = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
536
#  endif /* HAVE_EXTATTR_GET_LINK */
537
# elif defined(HAVE_SYS_XATTR_H)
538
#  if defined(HAVE_LGETXATTR)
539
  res = lgetxattr(path, name, val, valsz);
1✔
540
#  elif defined(XATTR_NOFOLLOW)
541
  res = getxattr(path, name, val, valsz, 0, XATTR_NOFOLLOW);
542
#  else
543
  res = getxattr(path, name, val, valsz);
544
#  endif /* HAVE_LGETXATTR */
545
# else
546
  errno = ENOSYS;
547
  res = -1;
548
# endif /* HAVE_SYS_XATTR_H */
549
#else
550
  (void) fs;
551
  (void) path;
552
  (void) name;
553
  (void) val;
554
  (void) valsz;
555

556
  errno = ENOSYS;
557
  res = -1;
558
#endif /* PR_USE_XATTR */
559

560
  return res;
1✔
561
}
562

563
static ssize_t sys_fgetxattr(pool *p, pr_fh_t *fh, int fd, const char *name,
1✔
564
    void *val, size_t valsz) {
565
  ssize_t res = -1;
1✔
566

567
  (void) p;
1✔
568

569
#if defined(PR_USE_XATTR)
570
# if defined(HAVE_SYS_EXTATTR_H)
571
  res = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name, val, valsz);
572
# elif defined(HAVE_SYS_XATTR_H)
573
#  if defined(XATTR_NOFOLLOW)
574
  res = fgetxattr(fd, name, val, valsz, 0, 0);
575
#  else
576
  res = fgetxattr(fd, name, val, valsz);
1✔
577
#  endif /* XATTR_NOFOLLOW */
578
# else
579
  errno = ENOSYS;
580
  res = -1;
581
# endif /* HAVE_SYS_XATTR_H */
582
#else
583
  (void) fh;
584
  (void) fd;
585
  (void) name;
586
  (void) val;
587
  (void) valsz;
588

589
  errno = ENOSYS;
590
  res = -1;
591
#endif /* PR_USE_XATTR */
592

593
  return res;
1✔
594
}
595

596
#if defined(PR_USE_XATTR)
597
static array_header *parse_xattr_namelist(pool *p, char *namelist, size_t sz) {
×
598
  array_header *names;
×
599
  char *ptr;
×
600

601
  names = make_array(p, 0, sizeof(char *));
×
602
  ptr = namelist;
×
603

604
# if defined(HAVE_SYS_EXTATTR_H)
605
  /* BSD style name lists use a one-byte length prefix (limiting xattr names
606
   * to a maximum length of 255 bytes), followed by the name, without any
607
   * terminating NUL.
608
   */
609
  while (sz > 0) {
610
    unsigned char len;
611

612
    pr_signals_handle();
613

614
    len = (unsigned char) *ptr;
615
    ptr++;
616
    sz--;
617

618
    *((char **) push_array(names)) = pstrndup(p, ptr, len);
619

620
    ptr += len;
621
    sz -= len;
622
  }
623

624
# elif defined(HAVE_SYS_XATTR_H)
625
  /* Linux/MacOSX style name lists use NUL-terminated xattr names. */
626
  while (sz > 0) {
×
627
    char *ptr2;
×
628
    size_t len;
×
629

630
    pr_signals_handle();
×
631

632
    for (ptr2 = ptr; *ptr2; ptr2++) {
×
633
    }
×
634
    len = ptr2 - ptr;
×
635
    *((char **) push_array(names)) = pstrndup(p, ptr, len);
×
636

637
    ptr = ptr2 + 1;
×
638
    sz -= (len + 1);
×
639
  }
640
# endif /* HAVE_SYS_XATTR_H */
641

642
  return names;
×
643
}
644

645
static ssize_t unix_listxattr(const char *path, char *namelist, size_t len) {
3✔
646
  ssize_t res = -1;
3✔
647

648
#if defined(HAVE_SYS_EXTATTR_H)
649
  res = extattr_list_file(path, EXTATTR_NAMESPACE_USER, namelist, len);
650
#elif defined(HAVE_SYS_XATTR_H)
651
# if defined(XATTR_NOFOLLOW)
652
  res = listxattr(path, namelist, len, 0);
653
# else
654
  res = listxattr(path, namelist, len);
6✔
655
# endif /* XATTR_NOFOLLOW */
656
#else
657
  errno = ENOSYS;
658
  res = -1;
659
#endif /* HAVE_SYS_XATTR_H */
660

661
  return res;
3✔
662
}
663

664
static ssize_t unix_llistxattr(const char *path, char *namelist, size_t len) {
1✔
665
  ssize_t res = -1;
1✔
666

667
# if defined(HAVE_SYS_EXTATTR_H)
668
#  if defined(HAVE_EXTATTR_LIST_LINK)
669
  res = extattr_list_link(path, EXTATTR_NAMESPACE_USER, namelist, len);
670
#  else
671
  res = extattr_list_file(path, EXTATTR_NAMESPACE_USER, namelist, len);
672
#  endif /* HAVE_EXTATTR_LIST_LINK */
673
# elif defined(HAVE_SYS_XATTR_H)
674
#  if defined(HAVE_LLISTXATTR)
675
  res = llistxattr(path, namelist, len);
676
#  elif defined(XATTR_NOFOLLOW)
677
  res = listxattr(path, namelist, len, XATTR_NOFOLLOW);
678
#  else
679
  res = listxattr(path, namelist, len);
2✔
680
#  endif /* XATTR_NOFOLLOW */
681
# else
682
  errno = ENOSYS;
683
  res = -1;
684
# endif /* HAVE_SYS_XATTR_H */
685

686
  return res;
1✔
687
}
688

689
static ssize_t unix_flistxattr(int fd, char *namelist, size_t len) {
3✔
690
  ssize_t res = -1;
3✔
691

692
# if defined(HAVE_SYS_EXTATTR_H)
693
  res = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, namelist, len);
694
# elif defined(HAVE_SYS_XATTR_H)
695
#  if defined(XATTR_NOFOLLOW)
696
  res = flistxattr(fd, namelist, len, 0);
697
#  else
698
  res = flistxattr(fd, namelist, len);
6✔
699
#  endif /* XATTR_NOFOLLOW */
700
# else
701
  errno = ENOSYS;
702
  res = -1;
703
# endif /* HAVE_SYS_XATTR_H */
704

705
  return res;
3✔
706
}
707
#endif /* PR_USE_XATTR */
708

709
static int sys_listxattr(pool *p, pr_fs_t *fs, const char *path,
3✔
710
    array_header **names) {
711
  ssize_t res = 0;
3✔
712
  char *namelist = NULL;
3✔
713
  size_t len = 0;
3✔
714

715
#if defined(PR_USE_XATTR)
716
  /* We need to handle the different formats of namelists that listxattr et al
717
   * can provide.  On *BSDs, the namelist buffer uses length prefixes and no
718
   * terminating NULs; on Linux/Mac, the namelist buffer uses ONLY
719
   * NUL-terminated names.
720
   *
721
   * Thus we ALWAYS provide all the available attribute names, by first
722
   * querying for the full namelist buffer size, allocating that out of
723
   * given pool, querying for the names (using the buffer), and then parsing
724
   * them into an array.
725
   */
726

727
  res = unix_listxattr(path, NULL, 0);
3✔
728
  if (res < 0) {
3✔
729
    return -1;
730
  }
731

732
  if (res == 0) {
2✔
733
    /* No extended attributes found. */
734
    pr_trace_msg(trace_channel, 15, "listxattr: found 0 xattr names for '%s'",
2✔
735
      path);
736
    return 0;
2✔
737
  }
738

739
  len = res;
×
740
  namelist = palloc(p, len);
×
741

742
  res = unix_listxattr(path, namelist, len);
×
743
  if (res < 0) {
×
744
    return -1;
745
  }
746

747
  if (res == 0) {
×
748
    /* No extended attributes found. */
749
    pr_trace_msg(trace_channel, 15, "listxattr: found 0 xattr names for '%s'",
×
750
      path);
751
    return 0;
×
752
  }
753

754
  *names = parse_xattr_namelist(p, namelist, len);
×
755
  if (pr_trace_get_level(trace_channel) >= 15) {
×
756
    register unsigned int i;
×
757
    unsigned int count;
×
758
    const char **attr_names;
×
759

760
    count = (*names)->nelts;
×
761
    attr_names = (*names)->elts;
×
762

763
    pr_trace_msg(trace_channel, 15, "listxattr: found %d xattr names for '%s'",
×
764
      count, path);
765
    for (i = 0; i < count; i++) {
×
766
      pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]);
×
767
    }
768
  }
769

770
  res = (*names)->nelts;
×
771

772
#else
773
  (void) fs;
774
  (void) path;
775
  (void) names;
776
  (void) namelist;
777
  (void) len;
778
  errno = ENOSYS;
779
  res = -1;
780
#endif /* PR_USE_XATTR */
781

782
  return (int) res;
×
783
}
784

785
static int sys_llistxattr(pool *p, pr_fs_t *fs, const char *path,
1✔
786
    array_header **names) {
787
  ssize_t res;
1✔
788
  char *namelist = NULL;
1✔
789
  size_t len = 0;
1✔
790

791
#if defined(PR_USE_XATTR)
792
  /* See sys_listxattr for a description of why we use this approach. */
793
  res = unix_llistxattr(path, NULL, 0);
1✔
794
  if (res < 0) {
1✔
795
    return -1;
796
  }
797

798
  if (res == 0) {
×
799
    /* No extended attributes found. */
800
    pr_trace_msg(trace_channel, 15, "llistxattr: found 0 xattr names for '%s'",
×
801
      path);
802
    return 0;
×
803
  }
804

805
  len = res;
×
806
  namelist = palloc(p, len);
×
807

808
  res = unix_llistxattr(path, namelist, len);
×
809
  if (res < 0) {
×
810
    return -1;
811
  }
812

813
  if (res == 0) {
×
814
    /* No extended attributes found. */
815
    pr_trace_msg(trace_channel, 15, "llistxattr: found 0 xattr names for '%s'",
×
816
      path);
817
    return 0;
×
818
  }
819

820
  *names = parse_xattr_namelist(p, namelist, len);
×
821
  if (pr_trace_get_level(trace_channel) >= 15) {
×
822
    register unsigned int i;
×
823
    unsigned int count;
×
824
    const char **attr_names;
×
825

826
    count = (*names)->nelts;
×
827
    attr_names = (*names)->elts;
×
828

829
    pr_trace_msg(trace_channel, 15, "llistxattr: found %d xattr names for '%s'",
×
830
      count, path);
831
    for (i = 0; i < count; i++) {
×
832
      pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]);
×
833
    }
834
  }
835

836
  res = (*names)->nelts;
×
837

838
#else
839
  (void) fs;
840
  (void) path;
841
  (void) names;
842
  (void) namelist;
843
  (void) len;
844
  errno = ENOSYS;
845
  res = -1;
846
#endif /* PR_USE_XATTR */
847

848
  return (int) res;
×
849
}
850

851
static int sys_flistxattr(pool *p, pr_fh_t *fh, int fd, array_header **names) {
3✔
852
  ssize_t res;
3✔
853
  char *namelist = NULL;
3✔
854
  size_t len = 0;
3✔
855

856
#if defined(PR_USE_XATTR)
857
  /* See sys_listxattr for a description of why we use this approach. */
858
  res = unix_flistxattr(fd, NULL, 0);
3✔
859
  if (res < 0) {
3✔
860
    return -1;
861
  }
862

863
  if (res == 0) {
3✔
864
    /* No extended attributes found. */
865
    pr_trace_msg(trace_channel, 15, "flistxattr: found 0 xattr names for '%s'",
3✔
866
      fh->fh_path);
867
    return 0;
3✔
868
  }
869

870
  len = res;
×
871
  namelist = palloc(p, len);
×
872

873
  res = unix_flistxattr(fd, namelist, len);
×
874
  if (res < 0) {
×
875
    return -1;
876
  }
877

878
  if (res == 0) {
×
879
    /* No extended attributes found. */
880
    pr_trace_msg(trace_channel, 15, "flistxattr: found 0 xattr names for '%s'",
×
881
      fh->fh_path);
882
    return 0;
×
883
  }
884

885
  *names = parse_xattr_namelist(p, namelist, len);
×
886
  if (pr_trace_get_level(trace_channel) >= 15) {
×
887
    register unsigned int i;
×
888
    unsigned int count;
×
889
    const char **attr_names;
×
890

891
    count = (*names)->nelts;
×
892
    attr_names = (*names)->elts;
×
893

894
    pr_trace_msg(trace_channel, 15, "flistxattr: found %d xattr names for '%s'",
×
895
      count, fh->fh_path);
896
    for (i = 0; i < count; i++) {
×
897
      pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]);
×
898
    }
899
  }
900

901
  res = (*names)->nelts;
×
902

903
#else
904
  (void) fh;
905
  (void) fd;
906
  (void) names;
907
  (void) namelist;
908
  (void) len;
909
  errno = ENOSYS;
910
  res = -1;
911
#endif /* PR_USE_XATTR */
912

913
  return (int) res;
×
914
}
915

916
static int sys_removexattr(pool *p, pr_fs_t *fs, const char *path,
1✔
917
    const char *name) {
918
  int res = -1;
1✔
919

920
  (void) p;
1✔
921

922
#if defined(PR_USE_XATTR)
923
# if defined(HAVE_SYS_EXTATTR_H)
924
  res = extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name);
925
# elif defined(HAVE_SYS_XATTR_H)
926
#  if defined(XATTR_NOFOLLOW)
927
  res = removexattr(path, name, 0);
928
#  else
929
  res = removexattr(path, name);
1✔
930
#  endif /* XATTR_NOFOLLOW */
931
# else
932
  errno = ENOSYS;
933
  res = -1;
934
# endif /* HAVE_SYS_XATTR_H */
935
#else
936
  (void) fs;
937
  (void) path;
938
  (void) name;
939

940
  errno = ENOSYS;
941
  res = -1;
942
#endif /* PR_USE_XATTR */
943

944
  return res;
1✔
945
}
946

947
static int sys_lremovexattr(pool *p, pr_fs_t *fs, const char *path,
1✔
948
    const char *name) {
949
  int res;
1✔
950

951
  (void) p;
1✔
952

953
#if defined(PR_USE_XATTR)
954
# if defined(HAVE_SYS_EXTATTR_H)
955
#  if defined(HAVE_EXTATTR_DELETE_LINK)
956
  res = extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
957
#  else
958
  res = extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name);
959
#  endif /* HAVE_EXTATTR_DELETE_LINK */
960
# elif defined(HAVE_SYS_XATTR_H)
961
#  if defined(HAVE_LREMOVEXATTR)
962
  res = lremovexattr(path, name);
1✔
963
#  elif defined(XATTR_NOFOLLOW)
964
  res = removexattr(path, name, XATTR_NOFOLLOW);
965
#  else
966
  res = removexattr(path, name);
967
#  endif /* XATTR_NOFOLLOW */
968
# else
969
  errno = ENOSYS;
970
  res = -1;
971
# endif /* HAVE_SYS_XATTR_H */
972
#else
973
  (void) fs;
974
  (void) path;
975
  (void) name;
976

977
  errno = ENOSYS;
978
  res = -1;
979
#endif /* PR_USE_XATTR */
980

981
  return res;
1✔
982
}
983

984
static int sys_fremovexattr(pool *p, pr_fh_t *fh, int fd, const char *name) {
1✔
985
  int res;
1✔
986

987
  (void) p;
1✔
988

989
#if defined(PR_USE_XATTR)
990
# if defined(HAVE_SYS_EXTATTR_H)
991
  res = extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, name);
992
# elif defined(HAVE_SYS_XATTR_H)
993
#  if defined(XATTR_NOFOLLOW)
994
  res = fremovexattr(fd, name, 0);
995
#  else
996
  res = fremovexattr(fd, name);
1✔
997
#  endif /* XATTR_NOFOLLOW */
998
# else
999
  errno = ENOSYS;
1000
  res = -1;
1001
# endif /* HAVE_SYS_XATTR_H */
1002
#else
1003
  (void) fh;
1004
  (void) fd;
1005
  (void) name;
1006

1007
  errno = ENOSYS;
1008
  res = -1;
1009
#endif /* PR_USE_XATTR */
1010

1011
  return res;
1✔
1012
}
1013

1014
#if defined(PR_USE_XATTR) && defined(HAVE_SYS_XATTR_H)
1015
/* Map the given flags onto the sys/xattr.h flags */
1016
static int get_setxattr_flags(int fsio_flags) {
5✔
1017
  int xattr_flags = 0;
5✔
1018

1019
  /* If both CREATE and REPLACE are set, use a value of zero; per the
1020
   * man pages, this value gives the desired "create or replace" semantics.
1021
   * Right?
1022
   */
1023

1024
  if (fsio_flags & PR_FSIO_XATTR_FL_CREATE) {
5✔
1025
#if defined(XATTR_CREATE)
1026
    xattr_flags = XATTR_CREATE;
5✔
1027
#endif /* XATTR_CREATE */
1028

1029
    if (fsio_flags & PR_FSIO_XATTR_FL_REPLACE) {
5✔
1030
      xattr_flags = 0;
×
1031
    }
1032

1033
  } else if (fsio_flags & PR_FSIO_XATTR_FL_REPLACE) {
×
1034
#if defined(XATTR_REPLACE)
1035
    xattr_flags = XATTR_REPLACE;
×
1036
#endif /* XATTR_REPLACE */
1037
  }
1038

1039
  return xattr_flags;
5✔
1040
}
1041
#endif /* PR_USE_XATTR and <sys/xattr.h> */
1042

1043
static int sys_setxattr(pool *p, pr_fs_t *fs, const char *path,
2✔
1044
    const char *name, void *val, size_t valsz, int flags) {
1045
  int res, xattr_flags = 0;
2✔
1046

1047
  (void) p;
2✔
1048

1049
#if defined(PR_USE_XATTR)
1050
# if defined(HAVE_SYS_EXTATTR_H)
1051
  (void) xattr_flags;
1052
  res = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
1053

1054
# elif defined(HAVE_SYS_XATTR_H)
1055
  xattr_flags = get_setxattr_flags(flags);
2✔
1056

1057
#  if defined(XATTR_NOFOLLOW)
1058
  res = setxattr(path, name, val, valsz, 0, xattr_flags);
1059
#  else
1060
  res = setxattr(path, name, val, valsz, xattr_flags);
2✔
1061
#  endif /* XATTR_NOFOLLOW */
1062
# else
1063
  errno = NOSYS;
1064
  res = -1;
1065
# endif /* HAVE_SYS_XATTR_H */
1066
#else
1067
  (void) fs;
1068
  (void) path;
1069
  (void) name;
1070
  (void) val;
1071
  (void) valsz;
1072
  (void) flags;
1073
  (void) xattr_flags;
1074

1075
  errno = ENOSYS;
1076
  res = -1;
1077
#endif /* PR_USE_XATTR */
1078

1079
  return res;
2✔
1080
}
1081

1082
static int sys_lsetxattr(pool *p, pr_fs_t *fs, const char *path,
2✔
1083
    const char *name, void *val, size_t valsz, int flags) {
1084
  int res, xattr_flags = 0;
2✔
1085

1086
  (void) p;
2✔
1087

1088
#if defined(PR_USE_XATTR)
1089
# if defined(HAVE_SYS_EXTATTR_H)
1090
  (void) xattr_flags;
1091
#  if defined(HAVE_EXTATTR_SET_LINK)
1092
  res = extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
1093
#  else
1094
  res = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
1095
#  endif /* HAVE_EXTATTR_SET_LINK */
1096
# elif defined(HAVE_SYS_XATTR_H)
1097
  xattr_flags = get_setxattr_flags(flags);
2✔
1098

1099
#  if defined(HAVE_LSETXATTR)
1100
  res = lsetxattr(path, name, val, valsz, xattr_flags);
1101
#  elif defined(XATTR_NOFOLLOW)
1102
  xattr_flags |= XATTR_NOFOLLOW;
1103
  res = setxattr(path, name, val, valsz, 0, xattr_flags);
1104
#  else
1105
  res = setxattr(path, name, val, valsz, xattr_flags);
2✔
1106
#  endif /* XATTR_NOFOLLOW */
1107
# else
1108
  errno = ENOSYS;
1109
  res = -1;
1110
# endif /* HAVE_SYS_XATTR_H */
1111
#else
1112
  (void) fs;
1113
  (void) path;
1114
  (void) name;
1115
  (void) val;
1116
  (void) valsz;
1117
  (void) flags;
1118
  (void) xattr_flags;
1119

1120
  errno = ENOSYS;
1121
  res = -1;
1122
#endif /* PR_USE_XATTR */
1123

1124
  return res;
2✔
1125
}
1126

1127
static int sys_fsetxattr(pool *p, pr_fh_t *fh, int fd, const char *name,
1✔
1128
    void *val, size_t valsz, int flags) {
1129
  int res, xattr_flags = 0;
1✔
1130

1131
  (void) p;
1✔
1132

1133
#if defined(PR_USE_XATTR)
1134
# if defined(HAVE_SYS_EXTATTR_H)
1135
  (void) xattr_flags;
1136
  res = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, name, val, valsz);
1137

1138
# elif defined(HAVE_SYS_XATTR_H)
1139
  xattr_flags = get_setxattr_flags(flags);
1✔
1140

1141
#  if defined(XATTR_NOFOLLOW)
1142
  res = fsetxattr(fd, name, val, valsz, 0, xattr_flags);
1143
#  else
1144
  res = fsetxattr(fd, name, val, valsz, xattr_flags);
1✔
1145
#  endif /* XATTR_NOFOLLOW */
1146
# else
1147
  errno = ENOSYS;
1148
  res = -1;
1149
# endif /* HAVE_SYS_XATTR_H */
1150
#else
1151
  (void) fh;
1152
  (void) fd;
1153
  (void) name;
1154
  (void) val;
1155
  (void) valsz;
1156
  (void) flags;
1157
  (void) xattr_flags;
1158

1159
  errno = ENOSYS;
1160
  res = -1;
1161
#endif /* PR_USE_XATTR */
1162

1163
  return res;
1✔
1164
}
1165

1166
static int sys_chroot(pr_fs_t *fs, const char *path) {
3✔
1167
  if (chroot(path) < 0) {
3✔
1168
    return -1;
1169
  }
1170

1171
  session.chroot_path = (char *) path;
×
1172
  return 0;
×
1173
}
1174

1175
static int sys_chdir(pr_fs_t *fs, const char *path) {
5✔
1176
  if (chdir(path) < 0) {
5✔
1177
    return -1;
1178
  }
1179

1180
  pr_fs_setcwd(path);
4✔
1181
  return 0;
4✔
1182
}
1183

1184
static void *sys_opendir(pr_fs_t *fs, const char *path) {
13✔
1185
  return opendir(path);
13✔
1186
}
1187

1188
static int sys_closedir(pr_fs_t *fs, void *dir) {
12✔
1189
  return closedir((DIR *) dir);
12✔
1190
}
1191

1192
static struct dirent *sys_readdir(pr_fs_t *fs, void *dir) {
65✔
1193
  return readdir((DIR *) dir);
65✔
1194
}
1195

1196
static int sys_mkdir(pr_fs_t *fs, const char *path, mode_t mode) {
8✔
1197
  int res;
8✔
1198

1199
  if (fsio_guard_chroot) {
8✔
1200
    res = chroot_allow_path(path);
1✔
1201
    if (res < 0) {
1✔
1202
      return -1;
1203
    }
1204
  }
1205

1206
  res = mkdir(path, mode);
7✔
1207
  return res;
7✔
1208
}
1209

1210
static int sys_rmdir(pr_fs_t *fs, const char *path) {
10✔
1211
  int res;
10✔
1212

1213
  if (fsio_guard_chroot) {
10✔
1214
    res = chroot_allow_path(path);
1✔
1215
    if (res < 0) {
1✔
1216
      return -1;
1217
    }
1218
  }
1219

1220
  res = rmdir(path);
9✔
1221
  return res;
9✔
1222
}
1223

1224
static int fs_cmp(const void *a, const void *b) {
4✔
1225
  pr_fs_t *fsa, *fsb;
4✔
1226

1227
  if (a == NULL) {
4✔
1228
    if (b == NULL) {
×
1229
      return 0;
1230
    }
1231

1232
    return 1;
×
1233
  }
1234

1235
  if (b == NULL) {
4✔
1236
    return -1;
1237
  }
1238

1239
  fsa = *((pr_fs_t **) a);
4✔
1240
  fsb = *((pr_fs_t **) b);
4✔
1241

1242
  return strcmp(fsa->fs_path, fsb->fs_path);
4✔
1243
}
1244

1245
/* Statcache stuff */
1246
struct fs_statcache {
1247
  xasetmember_t *next, *prev;
1248
  pool *sc_pool;
1249
  const char *sc_path;
1250
  struct stat sc_stat;
1251
  int sc_errno;
1252
  int sc_retval;
1253
  time_t sc_cached_ts;
1254
};
1255

1256
static const char *statcache_channel = "fs.statcache";
1257
static pool *statcache_pool = NULL;
1258
static unsigned int statcache_size = 0;
1259
static unsigned int statcache_max_age = 0;
1260
static unsigned int statcache_flags = 0;
1261

1262
/* We need to maintain two different caches: one for stat(2) data, and one
1263
 * for lstat(2) data.  For some files (e.g. symlinks), the struct stat data
1264
 * for the same path will be different for the two system calls.
1265
 */
1266
static pr_table_t *stat_statcache_tab = NULL;
1267
static xaset_t *stat_statcache_set = NULL;
1268
static pr_table_t *lstat_statcache_tab = NULL;
1269
static xaset_t *lstat_statcache_set = NULL;
1270

1271
#define fs_cache_lstat(f, p, s) cache_stat((f), (p), (s), FSIO_FILE_LSTAT)
1272
#define fs_cache_stat(f, p, s) cache_stat((f), (p), (s), FSIO_FILE_STAT)
1273

1274
static const struct fs_statcache *fs_statcache_get(pr_table_t *cache_tab,
1,593✔
1275
    xaset_t *cache_set, const char *path, size_t path_len, time_t now) {
1276
  const struct fs_statcache *sc = NULL;
1,593✔
1277

1278
  if (pr_table_count(cache_tab) == 0) {
1,593✔
1279
    errno = EPERM;
173✔
1280
    return NULL;
173✔
1281
  }
1282

1283
  sc = pr_table_get(cache_tab, path, NULL);
1,420✔
1284
  if (sc != NULL) {
1,420✔
1285
    time_t age;
59✔
1286

1287
    /* If this item hasn't expired yet, return it, otherwise, remove it. */
1288
    age = now - sc->sc_cached_ts;
59✔
1289
    if (age <= statcache_max_age) {
59✔
1290
      pr_trace_msg(statcache_channel, 19,
57✔
1291
        "using cached entry for '%s' (age %lu %s)", path,
1292
        (unsigned long) age, age != 1 ? "secs" : "sec");
1293
      return sc;
57✔
1294
    }
1295

1296
    pr_trace_msg(statcache_channel, 14,
2✔
1297
      "entry for '%s' expired (age %lu %s > max age %lu), removing", path,
1298
      (unsigned long) age, age != 1 ? "secs" : "sec",
1299
      (unsigned long) statcache_max_age);
1300
    (void) pr_table_remove(cache_tab, path, NULL);
2✔
1301
    (void) xaset_remove(cache_set, (xasetmember_t *) sc);
2✔
1302
    destroy_pool(sc->sc_pool);
2✔
1303
  }
1304

1305
  errno = ENOENT;
1,363✔
1306
  return NULL;
1,363✔
1307
}
1308

1309
static int fs_statcache_evict(pr_table_t *cache_tab, xaset_t *cache_set,
1✔
1310
    time_t now) {
1311
  time_t age;
1✔
1312
  xasetmember_t *item;
1✔
1313
  struct fs_statcache *sc;
1✔
1314

1315
  if (cache_set->xas_list == NULL) {
1✔
1316
    /* Should never happen. */
1317
    errno = EPERM;
×
1318
    return -1;
×
1319
  }
1320

1321
  /* We only need to remove the FIRST expired item; it should be the first
1322
   * item in the list, since we keep the list in insert (and thus expiry)
1323
   * order.
1324
   */
1325

1326
  item = cache_set->xas_list;
1✔
1327
  sc = (struct fs_statcache *) item;
1✔
1328
  age = now - sc->sc_cached_ts;
1✔
1329

1330
  if (age < statcache_max_age) {
1✔
1331
    errno = ENOENT;
×
1332
    return -1;
×
1333
  }
1334

1335
  pr_trace_msg(statcache_channel, 14,
1✔
1336
    "entry for '%s' expired (age %lu %s > max age %lu), evicting",
1337
    sc->sc_path, (unsigned long) age, age != 1 ? "secs" : "sec",
1338
    (unsigned long) statcache_max_age);
1339

1340
  (void) pr_table_remove(cache_tab, sc->sc_path, NULL);
1✔
1341
  (void) xaset_remove(cache_set, (xasetmember_t *) sc);
1✔
1342
  destroy_pool(sc->sc_pool);
1✔
1343
  return 0;
1✔
1344
}
1345

1346
/* Returns 1 if we successfully added a cache entry, 0 if not, and -1 if
1347
 * there was an error.
1348
 */
1349
static int fs_statcache_add(pr_table_t *cache_tab, xaset_t *cache_set,
1,536✔
1350
    const char *path, size_t path_len, struct stat *st, int xerrno,
1351
    int retval, time_t now) {
1352
  int res, table_count;
1,536✔
1353
  pool *sc_pool;
1,536✔
1354
  struct fs_statcache *sc;
1,536✔
1355

1356
  if (statcache_size == 0 ||
1,536✔
1357
      statcache_max_age == 0) {
125✔
1358
    /* Caching disabled; nothing to do here. */
1359
    return 0;
1360
  }
1361

1362
  table_count = pr_table_count(cache_tab);
125✔
1363
  if (table_count > 0 &&
125✔
1364
      (unsigned int) table_count >= statcache_size) {
71✔
1365
    /* We've reached capacity, and need to evict an item to make room. */
1366
    if (fs_statcache_evict(cache_tab, cache_set, now) < 0) {
1✔
1367
      pr_trace_msg(statcache_channel, 8,
×
1368
        "unable to evict enough items from the cache: %s", strerror(errno));
×
1369
    }
1370

1371
    /* We did not evict any items, and so are at capacity. */
1372
    return 0;
1✔
1373
  }
1374

1375
  sc_pool = make_sub_pool(statcache_pool);
124✔
1376
  pr_pool_tag(sc_pool, "FS statcache entry pool");
124✔
1377
  sc = pcalloc(sc_pool, sizeof(struct fs_statcache));
124✔
1378
  sc->sc_pool = sc_pool;
124✔
1379
  sc->sc_path = pstrndup(sc_pool, path, path_len);
124✔
1380
  memcpy(&(sc->sc_stat), st, sizeof(struct stat));
124✔
1381
  sc->sc_errno = xerrno;
124✔
1382
  sc->sc_retval = retval;
124✔
1383
  sc->sc_cached_ts = now;
124✔
1384

1385
  res = pr_table_add(cache_tab, sc->sc_path, sc, sizeof(struct fs_statcache *));
124✔
1386
  if (res < 0) {
124✔
1387
    int tmp_errno = errno;
×
1388

1389
    if (tmp_errno == EEXIST) {
×
1390
      res = 0;
×
1391
    }
1392

1393
    destroy_pool(sc->sc_pool);
×
1394
    errno = tmp_errno;
×
1395

1396
  } else {
1397
    xaset_insert_end(cache_set, (xasetmember_t *) sc);
124✔
1398
  }
1399

1400
  return (res == 0 ? 1 : res);
124✔
1401
}
1402

1403
static int cache_stat(pr_fs_t *fs, const char *path, struct stat *st,
1,593✔
1404
    unsigned int op) {
1405
  int res = -1, retval, xerrno = 0;
1,593✔
1406
  char cleaned_path[PR_TUNABLE_PATH_MAX+1], pathbuf[PR_TUNABLE_PATH_MAX+1];
1,593✔
1407
  int (*mystat)(pr_fs_t *, const char *, struct stat *) = NULL;
1,593✔
1408
  size_t path_len;
1,593✔
1409
  pr_table_t *cache_tab = NULL;
1,593✔
1410
  xaset_t *cache_set = NULL;
1,593✔
1411
  const struct fs_statcache *sc = NULL;
1,593✔
1412
  time_t now;
1,593✔
1413

1414
  now = time(NULL);
1,593✔
1415
  memset(cleaned_path, '\0', sizeof(cleaned_path));
1,593✔
1416
  memset(pathbuf, '\0', sizeof(pathbuf));
1,593✔
1417

1418
  if (fs->non_std_path == FALSE) {
1,593✔
1419
    /* Use only absolute path names.  Construct them, if given a relative
1420
     * path, based on cwd.  This obviates the need for something like
1421
     * realpath(3), which only introduces more stat(2) system calls.
1422
     */
1423
    if (*path != '/') {
1,593✔
1424
      size_t pathbuf_len;
6✔
1425

1426
      sstrcat(pathbuf, cwd, sizeof(pathbuf)-1);
6✔
1427
      pathbuf_len = cwd_len;
6✔
1428

1429
      /* If the cwd is "/", we don't need to duplicate the path separator.
1430
       * On some systems (e.g. Cygwin), this duplication can cause problems,
1431
       * as the path may then have different semantics.
1432
       */
1433
      if (strncmp(cwd, "/", 2) != 0) {
6✔
1434
        sstrcat(pathbuf + pathbuf_len, "/", sizeof(pathbuf) - pathbuf_len - 1);
6✔
1435
        pathbuf_len++;
6✔
1436
      }
1437

1438
      /* If the given directory is ".", then we don't need to append it. */
1439
      if (strncmp(path, ".", 2) != 0) {
6✔
1440
        sstrcat(pathbuf + pathbuf_len, path, sizeof(pathbuf)- pathbuf_len - 1);
6✔
1441
      }
1442

1443
    } else {
1444
      sstrncpy(pathbuf, path, sizeof(pathbuf)-1);
1,587✔
1445
    }
1446

1447
    pr_fs_clean_path2(pathbuf, cleaned_path, sizeof(cleaned_path)-1, 0);
1,593✔
1448

1449
  } else {
1450
    sstrncpy(cleaned_path, path, sizeof(cleaned_path)-1);
×
1451
  }
1452

1453
  /* Determine which filesystem function to use, stat() or lstat() */
1454
  if (op == FSIO_FILE_STAT) {
1,593✔
1455
    mystat = fs->stat ? fs->stat : sys_stat;
44✔
1456
    cache_tab = stat_statcache_tab;
44✔
1457
    cache_set = stat_statcache_set;
44✔
1458

1459
  } else {
1460
    mystat = fs->lstat ? fs->lstat : sys_lstat;
1,549✔
1461
    cache_tab = lstat_statcache_tab;
1,549✔
1462
    cache_set = lstat_statcache_set;
1,549✔
1463
  }
1464

1465
  path_len = strlen(cleaned_path);
1,593✔
1466

1467
  sc = fs_statcache_get(cache_tab, cache_set, cleaned_path, path_len, now);
1,593✔
1468
  if (sc != NULL) {
1,593✔
1469
    /* Update the given struct stat pointer with the cached info */
1470
    memcpy(st, &(sc->sc_stat), sizeof(struct stat));
57✔
1471

1472
    pr_trace_msg(trace_channel, 18,
57✔
1473
      "using cached stat for %s for path '%s' (retval %d, errno %s)",
1474
      op == FSIO_FILE_STAT ? "stat()" : "lstat()", path, sc->sc_retval,
57✔
1475
      strerror(sc->sc_errno));
57✔
1476

1477
    /* Use the cached errno as well */
1478
    errno = sc->sc_errno;
57✔
1479

1480
    return sc->sc_retval;
57✔
1481
  }
1482

1483
  pr_trace_msg(trace_channel, 8, "using %s %s for path '%s'",
3,034✔
1484
    fs->fs_name, op == FSIO_FILE_STAT ? "stat()" : "lstat()", path);
1485
  retval = mystat(fs, cleaned_path, st);
1,536✔
1486
  xerrno = errno;
1,536✔
1487

1488
  if (retval == 0) {
1,536✔
1489
    xerrno = 0;
1,502✔
1490
  }
1491

1492
  /* Update the cache */
1493
  res = fs_statcache_add(cache_tab, cache_set, cleaned_path, path_len, st,
1,536✔
1494
    xerrno, retval, now);
1495
  if (res < 0) {
1,536✔
1496
    pr_trace_msg(trace_channel, 8,
×
1497
      "error adding cached stat for '%s': %s", cleaned_path, strerror(errno));
1498

1499
  } else if (res > 0) {
1,536✔
1500
    pr_trace_msg(trace_channel, 18,
124✔
1501
      "added cached stat for path '%s' (retval %d, errno %s)", path,
1502
      retval, strerror(xerrno));
1503
  }
1504

1505
  if (retval < 0) {
1,536✔
1506
    errno = xerrno;
34✔
1507
  }
1508

1509
  return retval;
1510
}
1511

1512
/* Lookup routines */
1513

1514
/* Necessary prototype for static function */
1515
static pr_fs_t *lookup_file_canon_fs(const char *, char **, int);
1516

1517
/* lookup_dir_fs() is called when we want to perform some sort of directory
1518
 * operation on a directory or file.  A "closest" match algorithm is used.  If
1519
 * the lookup fails or is not "close enough" (i.e. the final target does not
1520
 * exactly match an existing filesystem handle) scan the list of fs_matches for
1521
 * matchable targets and call any callback functions, then rescan the pr_fs_t
1522
 * list.  The rescan is performed in case any modules registered pr_fs_ts
1523
 * during the hit.
1524
 */
1525
static pr_fs_t *lookup_dir_fs(const char *path, int op) {
2,204✔
1526
  char buf[PR_TUNABLE_PATH_MAX + 1], tmp_path[PR_TUNABLE_PATH_MAX + 1];
2,204✔
1527
  pr_fs_t *fs = NULL;
2,204✔
1528
  int exact = FALSE;
2,204✔
1529
  size_t tmp_pathlen = 0;
2,204✔
1530

1531
  memset(buf, '\0', sizeof(buf));
2,204✔
1532
  memset(tmp_path, '\0', sizeof(tmp_path));
2,204✔
1533
  sstrncpy(buf, path, sizeof(buf));
2,204✔
1534

1535
  /* Check if the given path is an absolute path.  Since there may be
1536
   * alternate fs roots, this is not a simple check.  If the path is
1537
   * not absolute, prepend the current location.
1538
   */
1539
  if (pr_fs_valid_path(path) < 0) {
2,204✔
1540
    if (pr_fs_dircat(tmp_path, sizeof(tmp_path), cwd, buf) < 0) {
5✔
1541
      return NULL;
1542
    }
1543

1544
  } else {
1545
    sstrncpy(tmp_path, buf, sizeof(tmp_path));
2,199✔
1546
  }
1547

1548
  /* Make sure that if this is a directory operation, the path being
1549
   * search ends in a trailing slash -- this is how files and directories
1550
   * are differentiated in the fs_map.
1551
   */
1552
  tmp_pathlen = strlen(tmp_path);
2,204✔
1553
  if ((FSIO_DIR_COMMON & op) &&
2,204✔
1554
      tmp_pathlen > 0 &&
1,672✔
1555
      tmp_pathlen < sizeof(tmp_path) &&
1,672✔
1556
      tmp_path[tmp_pathlen - 1] != '/') {
1,672✔
1557
    sstrcat(tmp_path, "/", sizeof(tmp_path));
1,390✔
1558
  }
1559

1560
  fs = pr_get_fs(tmp_path, &exact);
2,204✔
1561
  if (fs == NULL) {
2,204✔
1562
    fs = root_fs;
4✔
1563
  }
1564

1565
  return fs;
1566
}
1567

1568
/* lookup_file_fs() performs the same function as lookup_dir_fs, however
1569
 * because we are performing a file lookup, the target is the subdirectory
1570
 * _containing_ the actual target.  A basic optimization is used here,
1571
 * if the path contains no '/' characters, fs_cwd is returned.
1572
 */
1573
static pr_fs_t *lookup_file_fs(const char *path, char **deref, int op) {
380✔
1574
  pr_fs_t *fs = fs_cwd;
380✔
1575
  struct stat st;
380✔
1576
  int (*mystat)(pr_fs_t *, const char *, struct stat *) = NULL, res;
380✔
1577
  char linkbuf[PR_TUNABLE_PATH_MAX + 1];
380✔
1578

1579
  if (strchr(path, '/') != NULL) {
380✔
1580
    return lookup_dir_fs(path, op);
378✔
1581
  }
1582

1583
  /* Determine which function to use, stat() or lstat(). */
1584
  if (op == FSIO_FILE_STAT) {
2✔
1585
    while (fs != NULL &&
2✔
1586
           fs->fs_next != NULL &&
2✔
1587
           fs->stat == NULL) {
×
1588
      fs = fs->fs_next;
1589
    }
1590

1591
    mystat = fs->stat;
2✔
1592

1593
  } else {
1594
    while (fs != NULL &&
×
1595
           fs->fs_next != NULL &&
×
1596
           fs->lstat == NULL) {
×
1597
      fs = fs->fs_next;
1598
    }
1599

1600
    mystat = fs->lstat;
×
1601
  }
1602

1603
  if (mystat == NULL) {
2✔
1604
    errno = ENOSYS;
×
1605
    return NULL;
×
1606
  }
1607

1608
  res = mystat(fs, path, &st);
2✔
1609
  if (res < 0) {
2✔
1610
    return fs;
1611
  }
1612

1613
  if (!S_ISLNK(st.st_mode)) {
×
1614
    return fs;
1615
  }
1616

1617
  /* The given path is a symbolic link, in which case we need to find
1618
   * the actual path referenced, and return an pr_fs_t for _that_ path
1619
   */
1620

1621
  /* Three characters are reserved at the end of linkbuf for some path
1622
   * characters (and a trailing NUL).
1623
   */
1624
  if (fs->readlink != NULL) {
×
1625
    res = (fs->readlink)(fs, path, &linkbuf[2], sizeof(linkbuf)-3);
×
1626

1627
  } else {
1628
    errno = ENOSYS;
×
1629
    res = -1;
×
1630
  }
1631

1632
  if (res != -1) {
×
1633
    linkbuf[res] = '\0';
×
1634

1635
    if (strchr(linkbuf, '/') == NULL) {
×
1636
      if (res + 3 > PR_TUNABLE_PATH_MAX) {
×
1637
        res = PR_TUNABLE_PATH_MAX - 3;
1638
      }
1639

1640
      memmove(&linkbuf[2], linkbuf, res + 1);
×
1641

1642
      linkbuf[res+2] = '\0';
×
1643
      linkbuf[0] = '.';
×
1644
      linkbuf[1] = '/';
×
1645
      return lookup_file_canon_fs(linkbuf, deref, op);
×
1646
    }
1647
  }
1648

1649
  /* What happens if fs_cwd->readlink is NULL, or readlink() returns -1?
1650
   * I guess, for now, we punt, and return fs_cwd.
1651
   */
1652
  return fs_cwd;
×
1653
}
1654

1655
static pr_fs_t *lookup_file_canon_fs(const char *path, char **deref, int op) {
11✔
1656
  static char workpath[PR_TUNABLE_PATH_MAX + 1];
11✔
1657

1658
  memset(workpath,'\0',sizeof(workpath));
11✔
1659

1660
  if (pr_fs_resolve_partial(path, workpath, sizeof(workpath)-1,
11✔
1661
      FSIO_FILE_OPEN) == -1) {
1662
    if (*path == '/' || *path == '~') {
2✔
1663
      if (pr_fs_interpolate(path, workpath, sizeof(workpath)-1) != -1) {
2✔
1664
        sstrncpy(workpath, path, sizeof(workpath));
2✔
1665
      }
1666

1667
    } else {
1668
      if (pr_fs_dircat(workpath, sizeof(workpath), cwd, path) < 0) {
×
1669
        return NULL;
1670
      }
1671
    }
1672
  }
1673

1674
  if (deref) {
11✔
1675
    *deref = workpath;
11✔
1676
  }
1677

1678
  return lookup_file_fs(workpath, deref, op);
11✔
1679
}
1680

1681
/* FS Statcache API */
1682

1683
static void statcache_dumpf(const char *fmt, ...) {
4✔
1684
  char buf[PR_TUNABLE_BUFFER_SIZE];
4✔
1685
  va_list msg;
4✔
1686

1687
  memset(buf, '\0', sizeof(buf));
4✔
1688

1689
  va_start(msg, fmt);
4✔
1690
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
4✔
1691
  va_end(msg);
4✔
1692

1693
  buf[sizeof(buf)-1] = '\0';
4✔
1694
  (void) pr_trace_msg(statcache_channel, 9, "%s", buf);
4✔
1695
}
4✔
1696

1697
void pr_fs_statcache_dump(void) {
1✔
1698
  pr_table_dump(statcache_dumpf, stat_statcache_tab);
1✔
1699
  pr_table_dump(statcache_dumpf, lstat_statcache_tab);
1✔
1700
}
1✔
1701

1702
void pr_fs_statcache_free(void) {
4✔
1703
  if (stat_statcache_tab != NULL) {
4✔
1704
    int size;
4✔
1705

1706
    size = pr_table_count(stat_statcache_tab);
4✔
1707
    pr_trace_msg(statcache_channel, 11,
7✔
1708
      "resetting stat(2) statcache (clearing %d %s)", size,
1709
      size != 1 ? "entries" : "entry");
1710
    pr_table_empty(stat_statcache_tab);
4✔
1711
    pr_table_free(stat_statcache_tab);
4✔
1712
    stat_statcache_tab = NULL;
4✔
1713
    stat_statcache_set = NULL;
4✔
1714
  }
1715

1716
  if (lstat_statcache_tab != NULL) {
4✔
1717
    int size;
4✔
1718

1719
    size = pr_table_count(lstat_statcache_tab);
4✔
1720
    pr_trace_msg(statcache_channel, 11,
5✔
1721
      "resetting lstat(2) statcache (clearing %d %s)", size,
1722
      size != 1 ? "entries" : "entry");
1723
    pr_table_empty(lstat_statcache_tab);
4✔
1724
    pr_table_free(lstat_statcache_tab);
4✔
1725
    lstat_statcache_tab = NULL;
4✔
1726
    lstat_statcache_set = NULL;
4✔
1727
  }
1728

1729
  /* Note: we do not need to explicitly destroy each entry in the statcache
1730
   * tables, since ALL entries are allocated out of this statcache_pool.
1731
   * And we destroy this pool here.  Much easier cleanup that way.
1732
   */
1733
  if (statcache_pool != NULL) {
4✔
1734
    destroy_pool(statcache_pool);
4✔
1735
    statcache_pool = NULL;
4✔
1736
  }
1737
}
4✔
1738

1739
void pr_fs_statcache_reset(void) {
4✔
1740
  pr_fs_statcache_free();
4✔
1741

1742
  if (statcache_pool == NULL) {
4✔
1743
    statcache_pool = make_sub_pool(permanent_pool);
4✔
1744
    pr_pool_tag(statcache_pool, "FS Statcache Pool");
4✔
1745
  }
1746

1747
  stat_statcache_tab = pr_table_alloc(statcache_pool, 0);
4✔
1748
  stat_statcache_set = xaset_create(statcache_pool, NULL);
4✔
1749

1750
  lstat_statcache_tab = pr_table_alloc(statcache_pool, 0);
4✔
1751
  lstat_statcache_set = xaset_create(statcache_pool, NULL);
4✔
1752
}
4✔
1753

1754
int pr_fs_statcache_set_policy(unsigned int size, unsigned int max_age,
330✔
1755
    unsigned int flags) {
1756

1757
  statcache_size = size;
330✔
1758
  statcache_max_age = max_age;
330✔
1759
  statcache_flags = flags;
330✔
1760

1761
  return 0;
330✔
1762
}
1763

1764
int pr_fs_clear_cache2(const char *path) {
230✔
1765
  int res;
230✔
1766

1767
  (void) pr_event_generate("fs.statcache.clear", path);
230✔
1768

1769
  if (pr_table_count(stat_statcache_tab) == 0 &&
421✔
1770
      pr_table_count(lstat_statcache_tab) == 0) {
191✔
1771
    return 0;
1772
  }
1773

1774
  if (path != NULL) {
70✔
1775
    char cleaned_path[PR_TUNABLE_PATH_MAX+1], pathbuf[PR_TUNABLE_PATH_MAX+1];
68✔
1776
    int lstat_count, stat_count;
68✔
1777

1778
    if (*path != '/') {
68✔
1779
      size_t pathbuf_len;
5✔
1780

1781
      memset(cleaned_path, '\0', sizeof(cleaned_path));
5✔
1782
      memset(pathbuf, '\0', sizeof(pathbuf));
5✔
1783

1784
      sstrcat(pathbuf, cwd, sizeof(pathbuf)-1);
5✔
1785
      pathbuf_len = cwd_len;
5✔
1786

1787
      if (strncmp(cwd, "/", 2) != 0) {
5✔
1788
        sstrcat(pathbuf + pathbuf_len, "/", sizeof(pathbuf) - pathbuf_len - 1);
4✔
1789
        pathbuf_len++;
4✔
1790
      }
1791

1792
      if (strncmp(path, ".", 2) != 0) {
5✔
1793
        sstrcat(pathbuf + pathbuf_len, path, sizeof(pathbuf)- pathbuf_len - 1);
5✔
1794
      }
1795

1796
    } else {
1797
      sstrncpy(pathbuf, path, sizeof(pathbuf)-1);
63✔
1798
    }
1799

1800
    pr_fs_clean_path2(pathbuf, cleaned_path, sizeof(cleaned_path)-1, 0);
68✔
1801

1802
    res = 0;
68✔
1803

1804
    stat_count = pr_table_exists(stat_statcache_tab, cleaned_path);
68✔
1805
    if (stat_count > 0) {
68✔
1806
      const struct fs_statcache *sc;
22✔
1807

1808
      sc = pr_table_remove(stat_statcache_tab, cleaned_path, NULL);
22✔
1809
      if (sc != NULL) {
22✔
1810
        (void) xaset_remove(stat_statcache_set, (xasetmember_t *) sc);
22✔
1811
        destroy_pool(sc->sc_pool);
22✔
1812
      }
1813

1814
      pr_trace_msg(statcache_channel, 17, "cleared stat(2) entry for '%s'",
22✔
1815
        path);
1816
      res += stat_count;
22✔
1817
    }
1818

1819
    lstat_count = pr_table_exists(lstat_statcache_tab, cleaned_path);
68✔
1820
    if (lstat_count > 0) {
68✔
1821
      const struct fs_statcache *sc;
11✔
1822

1823
      sc = pr_table_remove(lstat_statcache_tab, cleaned_path, NULL);
11✔
1824
      if (sc != NULL) {
11✔
1825
        (void) xaset_remove(lstat_statcache_set, (xasetmember_t *) sc);
11✔
1826
        destroy_pool(sc->sc_pool);
11✔
1827
      }
1828

1829
      pr_trace_msg(statcache_channel, 17, "cleared lstat(2) entry for '%s'",
11✔
1830
        path);
1831
      res += lstat_count;
11✔
1832
    }
1833

1834
  } else {
1835
    /* Caller is requesting that we empty the entire cache. */
1836
    pr_fs_statcache_reset();
2✔
1837
    res = 0;
2✔
1838
  }
1839

1840
  return res;
1841
}
1842

1843
void pr_fs_clear_cache(void) {
4✔
1844
  (void) pr_fs_clear_cache2(NULL);
4✔
1845
}
4✔
1846

1847
/* FS functions proper */
1848

1849
int pr_fs_copy_file2(const char *src, const char *dst, int flags,
9✔
1850
    void (*progress_cb)(int)) {
1851
  pr_fh_t *src_fh, *dst_fh;
9✔
1852
  struct stat src_st, dst_st;
9✔
1853
  char *buf;
9✔
1854
  size_t bufsz;
9✔
1855
  int dst_existed = FALSE, res;
9✔
1856
#ifdef PR_USE_XATTR
1857
  array_header *xattrs = NULL;
9✔
1858
#endif /* PR_USE_XATTR */
1859

1860
  if (src == NULL ||
9✔
1861
      dst == NULL) {
9✔
1862
    errno = EINVAL;
2✔
1863
    return -1;
2✔
1864
  }
1865

1866
  copy_iter_count = 0;
7✔
1867

1868
  /* Use a nonblocking open() for the path; it could be a FIFO, and we don't
1869
   * want to block forever if the other end of the FIFO is not running.
1870
   */
1871
  src_fh = pr_fsio_open(src, O_RDONLY|O_NONBLOCK);
7✔
1872
  if (src_fh == NULL) {
7✔
1873
    int xerrno = errno;
1✔
1874

1875
    pr_log_pri(PR_LOG_WARNING, "error opening source file '%s' "
1✔
1876
      "for copying: %s", src, strerror(xerrno));
1877

1878
    errno = xerrno;
1✔
1879
    return -1;
1✔
1880
  }
1881

1882
  /* Do not allow copying of directories. open(2) may not fail when
1883
   * opening the source path, since it is only doing a read-only open,
1884
   * which does work on directories.
1885
   */
1886

1887
  /* This should never fail. */
1888
  if (pr_fsio_fstat(src_fh, &src_st) < 0) {
6✔
1889
    pr_trace_msg(trace_channel, 3,
×
1890
      "error fstat'ing '%s': %s", src, strerror(errno));
×
1891
  }
1892

1893
  if (S_ISDIR(src_st.st_mode)) {
6✔
1894
    int xerrno = EISDIR;
1✔
1895

1896
    pr_fsio_close(src_fh);
1✔
1897

1898
    pr_log_pri(PR_LOG_WARNING, "warning: cannot copy source '%s': %s", src,
1✔
1899
      strerror(xerrno));
1900

1901
    errno = xerrno;
1✔
1902
    return -1;
1✔
1903
  }
1904

1905
  if (pr_fsio_set_block(src_fh) < 0) {
5✔
1906
    pr_trace_msg(trace_channel, 3,
×
1907
      "error putting '%s' into blocking mode: %s", src, strerror(errno));
×
1908
  }
1909

1910
  /* We use stat() here, not lstat(), since open() would follow a symlink
1911
   * to its target, and what we really want to know here is whether the
1912
   * ultimate destination file exists or not.
1913
   */
1914
  pr_fs_clear_cache2(dst);
5✔
1915
  if (pr_fsio_stat(dst, &dst_st) == 0) {
5✔
1916
    if (S_ISDIR(dst_st.st_mode)) {
2✔
1917
      int xerrno = EISDIR;
1✔
1918

1919
      (void) pr_fsio_close(src_fh);
1✔
1920

1921
      pr_log_pri(PR_LOG_WARNING,
1✔
1922
        "warning: cannot copy to destination '%s': %s", dst, strerror(xerrno));
1923

1924
      errno = xerrno;
1✔
1925
      return -1;
1✔
1926
    }
1927

1928
    dst_existed = TRUE;
1✔
1929
    pr_fs_clear_cache2(dst);
1✔
1930
  }
1931

1932
  /* Use a nonblocking open() for the path; it could be a FIFO, and we don't
1933
   * want to block forever if the other end of the FIFO is not running.
1934
   */
1935
  dst_fh = pr_fsio_open(dst, O_WRONLY|O_CREAT|O_NONBLOCK);
4✔
1936
  if (dst_fh == NULL) {
4✔
1937
    int xerrno = errno;
1✔
1938

1939
    (void) pr_fsio_close(src_fh);
1✔
1940

1941
    pr_log_pri(PR_LOG_WARNING, "error opening destination file '%s' "
1✔
1942
      "for copying: %s", dst, strerror(xerrno));
1943

1944
    errno = xerrno;
1✔
1945
    return -1;
1✔
1946
  }
1947

1948
  if (pr_fsio_set_block(dst_fh) < 0) {
3✔
1949
    pr_trace_msg(trace_channel, 3,
×
1950
      "error putting '%s' into blocking mode: %s", dst, strerror(errno));
×
1951
  }
1952

1953
  /* Stat the source file to find its optimal copy block size. */
1954
  if (pr_fsio_fstat(src_fh, &src_st) < 0) {
3✔
1955
    int xerrno = errno;
×
1956

1957
    pr_log_pri(PR_LOG_WARNING, "error checking source file '%s' "
×
1958
      "for copying: %s", src, strerror(xerrno));
1959

1960
    (void) pr_fsio_close(src_fh);
×
1961
    (void) pr_fsio_close(dst_fh);
×
1962

1963
    /* Don't unlink the destination file if it already existed. */
1964
    if (!dst_existed) {
×
1965
      if (!(flags & PR_FSIO_COPY_FILE_FL_NO_DELETE_ON_FAILURE)) {
×
1966
        if (pr_fsio_unlink(dst) < 0) {
×
1967
          pr_trace_msg(trace_channel, 12,
×
1968
            "error deleting failed copy of '%s': %s", dst, strerror(errno));
1969
        }
1970
      }
1971
    }
1972

1973
    errno = xerrno;
×
1974
    return -1;
×
1975
  }
1976

1977
  if (pr_fsio_fstat(dst_fh, &dst_st) == 0) {
3✔
1978

1979
    /* Check to see if the source and destination paths are identical.
1980
     * We wait until now, rather than simply comparing the path strings
1981
     * earlier, in order to do stats on the paths and compare things like
1982
     * file size, mtime, inode, etc.
1983
     */
1984

1985
    if (strcmp(src, dst) == 0 &&
3✔
1986
        src_st.st_dev == dst_st.st_dev &&
1✔
1987
        src_st.st_ino == dst_st.st_ino &&
1✔
1988
        src_st.st_size == dst_st.st_size &&
1✔
1989
        src_st.st_mtime == dst_st.st_mtime) {
1✔
1990

1991
      (void) pr_fsio_close(src_fh);
1✔
1992
      (void) pr_fsio_close(dst_fh);
1✔
1993

1994
      /* No need to copy the same file. */
1995
      return 0;
1✔
1996
    }
1997
  }
1998

1999
  bufsz = src_st.st_blksize;
2✔
2000
  buf = malloc(bufsz);
2✔
2001
  if (buf == NULL) {
2✔
2002
    pr_log_pri(PR_LOG_ALERT, "Out of memory!");
×
2003
    exit(1);
×
2004
  }
2005

2006
#ifdef S_ISFIFO
2007
  if (!S_ISFIFO(dst_st.st_mode)) {
2✔
2008
    /* Make sure the destination file starts with a zero size. */
2009
    pr_fsio_truncate(dst, 0);
2✔
2010
  }
2011
#endif
2012

2013
  while ((res = pr_fsio_read(src_fh, buf, bufsz)) > 0) {
4✔
2014
    size_t datalen;
2✔
2015
    off_t offset;
2✔
2016

2017
    pr_signals_handle();
2✔
2018

2019
    /* Be sure to handle short writes. */
2020
    datalen = res;
2✔
2021
    offset = 0;
2✔
2022

2023
    while (datalen > 0) {
2✔
2024
      res = pr_fsio_write(dst_fh, buf + offset, datalen);
2✔
2025
      if (res < 0) {
2✔
2026
        int xerrno = errno;
×
2027

2028
        if (xerrno == EINTR ||
×
2029
            xerrno == EAGAIN) {
×
2030
          pr_signals_handle();
×
2031
          continue;
×
2032
        }
2033

2034
        (void) pr_fsio_close(src_fh);
×
2035
        (void) pr_fsio_close(dst_fh);
×
2036

2037
        /* Don't unlink the destination file if it already existed. */
2038
        if (!dst_existed) {
×
2039
          if (!(flags & PR_FSIO_COPY_FILE_FL_NO_DELETE_ON_FAILURE)) {
×
2040
            if (pr_fsio_unlink(dst) < 0) {
×
2041
              pr_trace_msg(trace_channel, 12,
×
2042
                "error deleting failed copy of '%s': %s", dst, strerror(errno));
2043
            }
2044
          }
2045
        }
2046

2047
        pr_log_pri(PR_LOG_WARNING, "error copying to '%s': %s", dst,
×
2048
          strerror(xerrno));
2049
        free(buf);
×
2050

2051
        errno = xerrno;
×
2052
        return -1;
×
2053
      }
2054

2055
      if (progress_cb != NULL) {
2✔
2056
        (progress_cb)(res);
1✔
2057

2058
      } else {
2059
        copy_progress_cb(res);
1✔
2060
      }
2061

2062
      if ((size_t) res == datalen) {
2✔
2063
        break;
2064
      }
2065

2066
      offset += res;
×
2067
      datalen -= res;
×
2068
    }
2069
  }
2070

2071
  free(buf);
2✔
2072

2073
#if defined(HAVE_POSIX_ACL) && defined(PR_USE_FACL)
2074
  {
2075
    /* Copy any ACLs from the source file to the destination file as well. */
2076
# if defined(HAVE_BSD_POSIX_ACL)
2077
    acl_t facl, facl_dup = NULL;
2078
    int have_facl = FALSE, have_dup = FALSE;
2079

2080
    facl = acl_get_fd(PR_FH_FD(src_fh));
2081
    if (facl) {
2082
      have_facl = TRUE;
2083
    }
2084

2085
    if (have_facl) {
2086
      facl_dup = acl_dup(facl);
2087
    }
2088

2089
    if (facl_dup) {
2090
      have_dup = TRUE;
2091
    }
2092

2093
    if (have_dup &&
2094
        acl_set_fd(PR_FH_FD(dst_fh), facl_dup) < 0) {
2095
      pr_log_debug(DEBUG3, "error applying ACL to destination file: %s",
2096
        strerror(errno));
2097
    }
2098

2099
    if (have_dup) {
2100
      acl_free(facl_dup);
2101
    }
2102
# elif defined(HAVE_LINUX_POSIX_ACL)
2103

2104
#  if defined(HAVE_PERM_COPY_FD)
2105
    /* Linux provides the handy perm_copy_fd(3) function in its libacl
2106
     * library just for this purpose.
2107
     */
2108
    if (perm_copy_fd(src, PR_FH_FD(src_fh), dst, PR_FH_FD(dst_fh), NULL) < 0) {
2109
      pr_log_debug(DEBUG3, "error copying ACL to destination file: %s",
2110
        strerror(errno));
2111
    }
2112

2113
#  else
2114
    acl_t src_acl = acl_get_fd(PR_FH_FD(src_fh));
2✔
2115
    if (src_acl == NULL) {
2✔
2116
      pr_log_debug(DEBUG3, "error obtaining ACL for fd %d: %s",
×
2117
        PR_FH_FD(src_fh), strerror(errno));
×
2118

2119
    } else {
2120
      if (acl_set_fd(PR_FH_FD(dst_fh), src_acl) < 0) {
2✔
2121
        pr_log_debug(DEBUG3, "error setting ACL on fd %d: %s",
×
2122
          PR_FH_FD(dst_fh), strerror(errno));
×
2123

2124
      } else {
2125
        acl_free(src_acl);
2✔
2126
      }
2127
    }
2128

2129
#  endif /* !HAVE_PERM_COPY_FD */
2130

2131
# elif defined(HAVE_SOLARIS_POSIX_ACL)
2132
    int nents;
2133

2134
    nents = facl(PR_FH_FD(src_fh), GETACLCNT, 0, NULL);
2135
    if (nents < 0) {
2136
      pr_log_debug(DEBUG3, "error getting source file ACL count: %s",
2137
        strerror(errno));
2138

2139
    } else {
2140
      aclent_t *acls;
2141

2142
      acls = malloc(sizeof(aclent_t) * nents);
2143
      if (acls == NULL) {
2144
        pr_log_pri(PR_LOG_ALERT, "Out of memory!");
2145
        exit(1);
2146
      }
2147

2148
      if (facl(PR_FH_FD(src_fh), GETACL, nents, acls) < 0) {
2149
        pr_log_debug(DEBUG3, "error getting source file ACLs: %s",
2150
          strerror(errno));
2151

2152
      } else {
2153
        if (facl(PR_FH_FD(dst_fh), SETACL, nents, acls) < 0) {
2154
          pr_log_debug(DEBUG3, "error setting dest file ACLs: %s",
2155
            strerror(errno));
2156
        }
2157
      }
2158

2159
      free(acls);
2160
    }
2161
# endif /* HAVE_SOLARIS_POSIX_ACL && PR_USE_FACL */
2162
  }
2163
#endif /* HAVE_POSIX_ACL */
2164

2165
#ifdef PR_USE_XATTR
2166
  /* Copy any xattrs that the source file may have. We'll use the
2167
   * destination file handle's pool for our xattr allocations.
2168
   */
2169
  if (pr_fsio_flistxattr(dst_fh->fh_pool, src_fh, &xattrs) > 0) {
2✔
2170
    register unsigned int i;
×
2171
    const char **names;
×
2172

2173
    names = xattrs->elts;
×
2174
    for (i = 0; i < xattrs->nelts; i++) {
×
2175
      ssize_t valsz;
×
2176

2177
      /* First, find out how much memory we need for this attribute's
2178
       * value.
2179
       */
2180
      valsz = pr_fsio_fgetxattr(dst_fh->fh_pool, src_fh, names[i], NULL, 0);
×
2181
      if (valsz > 0) {
×
2182
        void *val;
×
2183
        ssize_t sz;
×
2184

2185
        val = palloc(dst_fh->fh_pool, valsz);
×
2186
        sz = pr_fsio_fgetxattr(dst_fh->fh_pool, src_fh, names[i], val, valsz);
×
2187
        if (sz > 0) {
×
2188
          sz = pr_fsio_fsetxattr(dst_fh->fh_pool, dst_fh, names[i], val,
×
2189
            valsz, 0);
2190
          if (sz < 0 &&
×
2191
              errno != ENOSYS) {
×
2192
            pr_trace_msg(trace_channel, 7,
×
2193
              "error copying xattr '%s' (%lu bytes) from '%s' to '%s': %s",
2194
              names[i], (unsigned long) valsz, src, dst, strerror(errno));
2195
          }
2196
        }
2197
      }
2198
    }
2199
  }
2200
#endif /* PR_USE_XATTR */
2201

2202
  (void) pr_fsio_close(src_fh);
2✔
2203

2204
  if (progress_cb != NULL) {
2✔
2205
    (progress_cb)(0);
1✔
2206

2207
  } else {
2208
    copy_progress_cb(0);
1✔
2209
  }
2210

2211
  res = pr_fsio_close(dst_fh);
2✔
2212
  if (res < 0) {
2✔
2213
    int xerrno = errno;
×
2214

2215
    /* Don't unlink the destination file if it already existed. */
2216
    if (!dst_existed) {
×
2217
      if (!(flags & PR_FSIO_COPY_FILE_FL_NO_DELETE_ON_FAILURE)) {
×
2218
        if (pr_fsio_unlink(dst) < 0) {
×
2219
          pr_trace_msg(trace_channel, 12,
×
2220
            "error deleting failed copy of '%s': %s", dst, strerror(errno));
2221
        }
2222
      }
2223
    }
2224

2225
    pr_log_pri(PR_LOG_WARNING, "error closing '%s': %s", dst,
×
2226
      strerror(xerrno));
2227

2228
    errno = xerrno;
×
2229
  }
2230

2231
  return res;
2232
}
2233

2234
int pr_fs_copy_file(const char *src, const char *dst) {
8✔
2235
  return pr_fs_copy_file2(src, dst, 0, NULL);
8✔
2236
}
2237

2238
pr_fs_t *pr_register_fs2(pool *p, const char *name, const char *path,
16✔
2239
    int flags) {
2240
  pr_fs_t *fs = NULL;
16✔
2241
  int xerrno = 0;
16✔
2242

2243
  /* Sanity check */
2244
  if (p == NULL ||
16✔
2245
      name == NULL ||
16✔
2246
      path == NULL) {
2247
    errno = EINVAL;
6✔
2248
    return NULL;
6✔
2249
  }
2250

2251
  /* Instantiate an pr_fs_t */
2252
  fs = pr_create_fs(p, name);
10✔
2253
  xerrno = errno;
10✔
2254

2255
  if (fs == NULL) {
10✔
2256
    pr_trace_msg(trace_channel, 6, "error creating FS '%s': %s", name,
×
2257
      strerror(errno));
2258
    errno = xerrno;
×
2259
    return NULL;
×
2260
  }
2261

2262
  if (flags & PR_FSIO_REGISTER_FL_INHERIT_HANDLERS) {
10✔
2263
    pr_fs_t *curr_fs = NULL;
7✔
2264
    int match = FALSE;
7✔
2265

2266
    /* Note that we need to be aware of other modules' FS handlers, such
2267
     * as mod_vroot (see Issue #1764, #1780).
2268
     */
2269
    curr_fs = pr_get_fs(path, &match);
7✔
2270
    if (curr_fs != NULL) {
7✔
2271
      fs->fs_name = pstrcat(fs->fs_pool, name, "+", curr_fs->fs_name, NULL);
7✔
2272

2273
      /* Inherit all of the current FS handlers.  This makes it easy to
2274
       * preserve the functionality desired by all previously registered
2275
       * handlers.
2276
       */
2277
      fs->stat = curr_fs->stat;
7✔
2278
      fs->fstat = curr_fs->fstat;
7✔
2279
      fs->lstat = curr_fs->lstat;
7✔
2280
      fs->rename = curr_fs->rename;
7✔
2281
      fs->unlink = curr_fs->unlink;
7✔
2282
      fs->open = curr_fs->open;
7✔
2283
      fs->close = curr_fs->close;
7✔
2284
      fs->read = curr_fs->read;
7✔
2285
      fs->pread = curr_fs->pread;
7✔
2286
      fs->write = curr_fs->write;
7✔
2287
      fs->pwrite = curr_fs->pwrite;
7✔
2288
      fs->lseek = curr_fs->lseek;
7✔
2289
      fs->link = curr_fs->link;
7✔
2290
      fs->readlink = curr_fs->readlink;
7✔
2291
      fs->symlink = curr_fs->symlink;
7✔
2292
      fs->ftruncate = curr_fs->ftruncate;
7✔
2293
      fs->truncate = curr_fs->truncate;
7✔
2294
      fs->chmod = curr_fs->chmod;
7✔
2295
      fs->fchmod = curr_fs->fchmod;
7✔
2296
      fs->chown = curr_fs->chown;
7✔
2297
      fs->fchown = curr_fs->fchown;
7✔
2298
      fs->lchown = curr_fs->lchown;
7✔
2299
      fs->access = curr_fs->access;
7✔
2300
      fs->faccess = curr_fs->faccess;
7✔
2301
      fs->utimes = curr_fs->utimes;
7✔
2302
      fs->futimes = curr_fs->futimes;
7✔
2303
      fs->fsync = curr_fs->fsync;
7✔
2304

2305
      fs->chdir = curr_fs->chdir;
7✔
2306
      fs->chroot = curr_fs->chroot;
7✔
2307
      fs->opendir = curr_fs->opendir;
7✔
2308
      fs->closedir = curr_fs->closedir;
7✔
2309
      fs->readdir = curr_fs->readdir;
7✔
2310
      fs->mkdir = curr_fs->mkdir;
7✔
2311
      fs->rmdir = curr_fs->rmdir;
7✔
2312
    }
2313
  }
2314

2315
  if (pr_insert_fs(fs, path) == FALSE) {
10✔
2316
    xerrno = errno;
2✔
2317

2318
    pr_trace_msg(trace_channel, 4, "error inserting FS '%s' at path '%s'",
2✔
2319
      name, path);
2320

2321
    destroy_pool(fs->fs_pool);
2✔
2322
    errno = xerrno;
2✔
2323
    return NULL;
2✔
2324
  }
2325

2326
  return fs;
2327
}
2328

2329
pr_fs_t *pr_register_fs(pool *p, const char *name, const char *path) {
10✔
2330
  return pr_register_fs2(p, name, path, PR_FSIO_REGISTER_FL_INHERIT_HANDLERS);
10✔
2331
}
2332

2333
pr_fs_t *pr_create_fs(pool *p, const char *name) {
280✔
2334
  pr_fs_t *fs = NULL;
280✔
2335
  pool *fs_pool = NULL;
280✔
2336

2337
  /* Sanity check */
2338
  if (p == NULL ||
280✔
2339
      name == NULL) {
280✔
2340
    errno = EINVAL;
2✔
2341
    return NULL;
2✔
2342
  }
2343

2344
  /* Allocate a subpool, then allocate an pr_fs_t object from that subpool */
2345
  fs_pool = make_sub_pool(p);
278✔
2346
  pr_pool_tag(fs_pool, "FS Pool");
278✔
2347

2348
  fs = pcalloc(fs_pool, sizeof(pr_fs_t));
278✔
2349
  fs->fs_pool = fs_pool;
278✔
2350
  fs->fs_next = fs->fs_prev = NULL;
278✔
2351
  fs->fs_name = pstrdup(fs->fs_pool, name);
278✔
2352
  fs->fs_original_name = fs->fs_name;
278✔
2353
  fs->fs_next = root_fs;
278✔
2354
  fs->allow_xdev_link = TRUE;
278✔
2355
  fs->allow_xdev_rename = TRUE;
278✔
2356

2357
  /* This is NULL until set by pr_insert_fs() */
2358
  fs->fs_path = NULL;
278✔
2359

2360
  return fs;
278✔
2361
}
2362

2363
int pr_insert_fs(pr_fs_t *fs, const char *path) {
22✔
2364
  char cleaned_path[PR_TUNABLE_PATH_MAX] = {'\0'};
22✔
2365

2366
  if (fs == NULL ||
22✔
2367
      path == NULL) {
22✔
2368
    errno = EINVAL;
2✔
2369
    return -1;
2✔
2370
  }
2371

2372
  if (fs_map == NULL) {
20✔
2373
    pool *map_pool = make_sub_pool(permanent_pool);
11✔
2374
    pr_pool_tag(map_pool, "FSIO Map Pool");
11✔
2375

2376
    fs_map = make_array(map_pool, 0, sizeof(pr_fs_t *));
11✔
2377
  }
2378

2379
  /* Clean the path, but only if it starts with a '/'.  Non-local-filesystem
2380
   * paths may not want/need to be cleaned.
2381
   */
2382
  if (*path == '/') {
20✔
2383
    pr_fs_clean_path(path, cleaned_path, sizeof(cleaned_path));
36✔
2384

2385
    /* Cleaning the path may have removed a trailing slash, which the
2386
     * caller may actually have wanted.  Make sure one is present in
2387
     * the cleaned version, if it was present in the original version and
2388
     * is not present in the cleaned version.
2389
     */
2390
    if (path[strlen(path)-1] == '/') {
18✔
2391
      size_t len = strlen(cleaned_path);
2✔
2392

2393
      if (len > 1 &&
2✔
2394
          len < (PR_TUNABLE_PATH_MAX-3) &&
2✔
2395
          cleaned_path[len-1] != '/') {
2✔
2396
        cleaned_path[len] = '/';
2✔
2397
        cleaned_path[len+1] = '\0';
2✔
2398
      }
2399
    }
2400

2401
  } else {
2402
    sstrncpy(cleaned_path, path, sizeof(cleaned_path));
2✔
2403
  }
2404

2405
  if (fs->fs_path == NULL) {
20✔
2406
    fs->fs_path = pstrdup(fs->fs_pool, cleaned_path);
19✔
2407
  }
2408

2409
  /* Check for duplicates. */
2410
  if (fs_map->nelts > 0) {
20✔
2411
    pr_fs_t *fsi = NULL, **fs_objs = (pr_fs_t **) fs_map->elts;
9✔
2412
    register unsigned int i;
9✔
2413

2414
    for (i = 0; i < fs_map->nelts; i++) {
15✔
2415
      fsi = fs_objs[i];
11✔
2416

2417
      if (strcmp(fsi->fs_path, cleaned_path) == 0) {
11✔
2418
        /* An entry for this path already exists.  Make sure the FS being
2419
         * mounted is not the same as the one already present.
2420
         */
2421
        if (strcmp(fsi->fs_original_name, fs->fs_original_name) == 0) {
5✔
2422
          pr_log_pri(PR_LOG_NOTICE,
3✔
2423
            "error: duplicate fs paths not allowed: '%s'", cleaned_path);
2424
          errno = EEXIST;
3✔
2425
          return FALSE;
3✔
2426
        }
2427

2428
        /* "Push" the given FS on top of the existing one. */
2429
        fs->fs_next = fsi;
2✔
2430
        fsi->fs_prev = fs;
2✔
2431
        fs_objs[i] = fs;
2✔
2432

2433
        chk_fs_map = TRUE;
2✔
2434
        return TRUE;
2✔
2435
      }
2436
    }
2437
  }
2438

2439
  /* Push the new FS into the container, then resort the contents. */
2440
  *((pr_fs_t **) push_array(fs_map)) = fs;
15✔
2441

2442
  /* Sort the FSs in the map according to their paths, but only if there
2443
   * are more than one element in the array_header.
2444
   */
2445
  if (fs_map->nelts > 1) {
15✔
2446
    qsort(fs_map->elts, fs_map->nelts, sizeof(pr_fs_t *), fs_cmp);
4✔
2447
  }
2448

2449
  /* Set the flag so that the fs wrapper functions know that a new FS
2450
   * has been registered.
2451
   */
2452
  chk_fs_map = TRUE;
15✔
2453

2454
  return TRUE;
15✔
2455
}
2456

2457
pr_fs_t *pr_unmount_fs(const char *path, const char *name) {
152✔
2458
  pr_fs_t *fsi = NULL, **fs_objs = NULL;
152✔
2459
  register unsigned int i = 0;
152✔
2460

2461
  /* Sanity check */
2462
  if (path == NULL) {
152✔
2463
    errno = EINVAL;
2✔
2464
    return NULL;
2✔
2465
  }
2466

2467
  /* This should never be called before pr_register_fs(), but, just in case...*/
2468
  if (fs_map == NULL) {
150✔
2469
    errno = EACCES;
117✔
2470
    return NULL;
117✔
2471
  }
2472

2473
  fs_objs = (pr_fs_t **) fs_map->elts;
33✔
2474

2475
  for (i = 0; i < fs_map->nelts; i++) {
67✔
2476
    fsi = fs_objs[i];
46✔
2477

2478
    if (strcmp(fsi->fs_path, path) == 0 &&
46✔
2479
        (name ? strcmp(fsi->fs_original_name, name) == 0 : TRUE)) {
1✔
2480

2481
      /* Exact match -- remove this FS.  If there is an FS underneath, pop
2482
       * the top FS off the stack.  Otherwise, allocate a new map.  Then
2483
       * iterate through the old map, pushing all other FSs into the new map.
2484
       * Destroy the old map.  Move the new map into place.
2485
       */
2486

2487
      if (fsi->fs_next == NULL) {
12✔
2488
        register unsigned int j = 0;
×
2489
        pr_fs_t *tmp_fs, **old_objs = NULL;
×
2490
        pool *map_pool;
×
2491
        array_header *new_map;
×
2492

2493
        /* If removing this FS would leave an empty map, don't bother
2494
         * allocating a new one.
2495
         */
2496
        if (fs_map->nelts == 1) {
×
2497
          destroy_pool(fs_map->pool);
×
2498
          fs_map = NULL;
×
2499
          fs_cwd = root_fs;
×
2500

2501
          chk_fs_map = TRUE;
×
2502
          return NULL;
×
2503
        }
2504

2505
        map_pool = make_sub_pool(permanent_pool);
×
2506
        new_map = make_array(map_pool, 0, sizeof(pr_fs_t *));
×
2507

2508
        pr_pool_tag(map_pool, "FSIO Map Pool");
×
2509
        old_objs = (pr_fs_t **) fs_map->elts;
×
2510

2511
        for (j = 0; j < fs_map->nelts; j++) {
×
2512
          tmp_fs = old_objs[j];
×
2513

2514
          if (strcmp(tmp_fs->fs_path, path) != 0) {
×
2515
            *((pr_fs_t **) push_array(new_map)) = old_objs[j];
×
2516
          }
2517
        }
2518

2519
        destroy_pool(fs_map->pool);
×
2520
        fs_map = new_map;
×
2521

2522
        /* Don't forget to set the flag so that wrapper functions scan the
2523
         * new map.
2524
         */
2525
        chk_fs_map = TRUE;
×
2526

2527
        return fsi;
×
2528
      }
2529

2530
      /* "Pop" this FS off the stack. */
2531
      if (fsi->fs_next != NULL) {
12✔
2532
        fsi->fs_next->fs_prev = NULL;
12✔
2533
      }
2534
      fs_objs[i] = fsi->fs_next;
12✔
2535
      fsi->fs_next = fsi->fs_prev = NULL;
12✔
2536

2537
      chk_fs_map = TRUE;
12✔
2538
      return fsi;
12✔
2539
    }
2540
  }
2541

2542
  errno = ENOENT;
21✔
2543
  return NULL;
21✔
2544
}
2545

2546
pr_fs_t *pr_remove_fs(const char *path) {
145✔
2547
  return pr_unmount_fs(path, NULL);
15✔
2548
}
2549

2550
int pr_unregister_fs(const char *path) {
131✔
2551
  pr_fs_t *fs = NULL;
131✔
2552

2553
  if (path == NULL) {
131✔
2554
    errno = EINVAL;
1✔
2555
    return -1;
1✔
2556
  }
2557

2558
  /* Call pr_remove_fs() to get the fs for this path removed from the map. */
2559
  fs = pr_remove_fs(path);
130✔
2560
  if (fs != NULL) {
130✔
2561
    destroy_pool(fs->fs_pool);
3✔
2562
    return 0;
3✔
2563
  }
2564

2565
  errno = ENOENT;
127✔
2566
  return -1;
127✔
2567
}
2568

2569
/* This function returns the best pr_fs_t to handle the given path.  It will
2570
 * return NULL if there are no registered pr_fs_ts to handle the given path,
2571
 * in which case the default root_fs should be used.  This is so that
2572
 * functions can look to see if an pr_fs_t, other than the default, for a
2573
 * given path has been registered, if necessary.  If the return value is
2574
 * non-NULL, that will be a registered pr_fs_t to handle the given path.  In
2575
 * this case, if the exact argument is not NULL, it will either be TRUE,
2576
 * signifying that the returned pr_fs_t is an exact match for the given
2577
 * path, or FALSE, meaning the returned pr_fs_t is a "best match" -- most
2578
 * likely the pr_fs_t that handles the directory in which the given path
2579
 * occurs.
2580
 */
2581
pr_fs_t *pr_get_fs(const char *path, int *exact) {
2,216✔
2582
  pr_fs_t *fs = NULL, **fs_objs = NULL, *best_match_fs = NULL;
2,216✔
2583
  register unsigned int i = 0;
2,216✔
2584

2585
  /* Sanity check */
2586
  if (path == NULL) {
2,216✔
2587
    errno = EINVAL;
1✔
2588
    return NULL;
1✔
2589
  }
2590

2591
  /* Basic optimization -- if there're no elements in the fs_map,
2592
   * return the root_fs.
2593
   */
2594
  if (fs_map == NULL ||
2,215✔
2595
      fs_map->nelts == 0) {
8✔
2596
    return root_fs;
2,207✔
2597
  }
2598

2599
  fs_objs = (pr_fs_t **) fs_map->elts;
8✔
2600
  best_match_fs = root_fs;
8✔
2601

2602
  /* In order to handle deferred-resolution paths (eg "~" paths), the given
2603
   * path will need to be passed through dir_realpath(), if necessary.
2604
   *
2605
   * The chk_fs_map flag, if TRUE, should be cleared on return of this
2606
   * function -- all that flag says is, if TRUE, that this function _might_
2607
   * return something different than it did on a previous call.
2608
   */
2609

2610
  for (i = 0; i < fs_map->nelts; i++) {
12✔
2611
    int res = 0;
9✔
2612

2613
    fs = fs_objs[i];
9✔
2614

2615
    /* If the current pr_fs_t's path ends in a slash (meaning it is a
2616
     * directory, and it matches the first part of the given path,
2617
     * assume it to be the best pr_fs_t found so far.
2618
     */
2619
    if ((fs->fs_path)[strlen(fs->fs_path) - 1] == '/' &&
9✔
2620
        !strncmp(path, fs->fs_path, strlen(fs->fs_path))) {
4✔
2621
      best_match_fs = fs;
2✔
2622
    }
2623

2624
    res = strcmp(fs->fs_path, path);
9✔
2625
    if (res == 0) {
9✔
2626
      /* Exact match */
2627
      if (exact) {
2✔
2628
        *exact = TRUE;
2✔
2629
      }
2630

2631
      chk_fs_map = FALSE;
2✔
2632
      return fs;
2✔
2633
    }
2634

2635
    if (res > 0) {
7✔
2636
      if (exact != NULL) {
3✔
2637
        *exact = FALSE;
3✔
2638
      }
2639

2640
      chk_fs_map = FALSE;
3✔
2641

2642
      /* Gone too far - return the best-match pr_fs_t */
2643
      return best_match_fs;
3✔
2644
    }
2645
  }
2646

2647
  chk_fs_map = FALSE;
3✔
2648

2649
  if (exact != NULL) {
3✔
2650
    *exact = FALSE;
3✔
2651
  }
2652

2653
  /* Return best-match by default */
2654
  return best_match_fs;
2655
}
2656

2657
int pr_fs_setcwd(const char *dir) {
266✔
2658
  if (pr_fs_resolve_path(dir, cwd, sizeof(cwd)-1, FSIO_DIR_CHDIR) < 0) {
266✔
2659
    return -1;
2660
  }
2661

2662
  if (sstrncpy(cwd, dir, sizeof(cwd)) < 0) {
266✔
2663
    return -1;
2664
  }
2665

2666
  fs_cwd = lookup_dir_fs(cwd, FSIO_DIR_CHDIR);
266✔
2667
  cwd[sizeof(cwd) - 1] = '\0';
266✔
2668
  cwd_len = strlen(cwd);
266✔
2669

2670
  return 0;
266✔
2671
}
2672

2673
const char *pr_fs_getcwd(void) {
41✔
2674
  return cwd;
41✔
2675
}
2676

2677
const char *pr_fs_getvwd(void) {
8✔
2678
  return vwd;
8✔
2679
}
2680

2681
int pr_fs_dircat(char *buf, int buflen, const char *dir1, const char *dir2) {
36✔
2682
  /* Make temporary copies so that memory areas can overlap */
2683
  char *_dir1 = NULL, *_dir2 = NULL, *ptr = NULL;
36✔
2684
  size_t dir1len = 0, dir2len = 0;
36✔
2685

2686
  /* The shortest possible path is "/", which requires 2 bytes. */
2687

2688
  if (buf == NULL ||
36✔
2689
      buflen < 2 ||
36✔
2690
      dir1 == NULL ||
33✔
2691
      dir2 == NULL) {
33✔
2692
    errno = EINVAL;
3✔
2693
    return -1;
3✔
2694
  }
2695

2696
  /* This is a test to see if we've got reasonable directories to concatenate.
2697
   */
2698
  dir1len = strlen(dir1);
33✔
2699
  dir2len = strlen(dir2);
33✔
2700

2701
  /* If both strings are empty, then the "concatenation" becomes trivial. */
2702
  if (dir1len == 0 &&
33✔
2703
      dir2len == 0) {
2704
    buf[0] = '/';
1✔
2705
    buf[1] = '\0';
1✔
2706
    return 0;
1✔
2707
  }
2708

2709
  /* If dir2 is non-empty, but dir1 IS empty... */
2710
  if (dir1len == 0) {
32✔
2711
    sstrncpy(buf, dir2, buflen);
1✔
2712
    buflen -= dir2len;
1✔
2713
    sstrcat(buf, "/", buflen);
1✔
2714
    return 0;
1✔
2715
  }
2716

2717
  /* Likewise, if dir1 is non-empty, but dir2 IS empty... */
2718
  if (dir2len == 0) {
31✔
2719
    sstrncpy(buf, dir1, buflen);
1✔
2720
    buflen -= dir1len;
1✔
2721
    sstrcat(buf, "/", buflen);
1✔
2722
    return 0;
1✔
2723
  }
2724

2725
  if ((dir1len + dir2len + 1) >= PR_TUNABLE_PATH_MAX) {
30✔
2726
    errno = ENAMETOOLONG;
1✔
2727
    buf[0] = '\0';
1✔
2728
    return -1;
1✔
2729
  }
2730

2731
  _dir1 = strdup(dir1);
29✔
2732
  if (_dir1 == NULL) {
29✔
2733
    return -1;
2734
  }
2735

2736
  _dir2 = strdup(dir2);
29✔
2737
  if (_dir2 == NULL) {
29✔
2738
    int xerrno = errno;
×
2739

2740
    free(_dir1);
×
2741

2742
    errno = xerrno;
×
2743
    return -1;
×
2744
  }
2745

2746
  if (*_dir2 == '/') {
29✔
2747
    sstrncpy(buf, _dir2, buflen);
4✔
2748
    free(_dir1);
4✔
2749
    free(_dir2);
4✔
2750
    return 0;
4✔
2751
  }
2752

2753
  ptr = buf;
25✔
2754
  sstrncpy(ptr, _dir1, buflen);
25✔
2755
  ptr += dir1len;
25✔
2756
  buflen -= dir1len;
25✔
2757

2758
  if (buflen > 0 &&
25✔
2759
      *(_dir1 + (dir1len-1)) != '/') {
25✔
2760
    sstrcat(ptr, "/", buflen);
22✔
2761
    ptr += 1;
22✔
2762
    buflen -= 1;
22✔
2763
  }
2764

2765
  sstrcat(ptr, _dir2, buflen);
25✔
2766

2767
  if (*buf == '\0') {
25✔
2768
   *buf++ = '/';
×
2769
   *buf = '\0';
×
2770
  }
2771

2772
  free(_dir1);
25✔
2773
  free(_dir2);
25✔
2774

2775
  return 0;
25✔
2776
}
2777

2778
/* This function performs any tilde expansion needed and then returns the
2779
 * resolved path, if any.
2780
 *
2781
 * Returns: -1 (errno = ENOENT): user does not exist
2782
 *           0 : no interpolation done (path exists)
2783
 *           1 : interpolation done
2784
 */
2785
int pr_fs_interpolate(const char *path, char *buf, size_t buflen) {
339✔
2786
  char *ptr = NULL;
339✔
2787
  size_t currlen, pathlen;
339✔
2788
  char user[PR_TUNABLE_LOGIN_MAX+1];
339✔
2789

2790
  if (path == NULL ||
339✔
2791
      buf == NULL ||
339✔
2792
      buflen == 0) {
2793
    errno = EINVAL;
3✔
2794
    return -1;
3✔
2795
  }
2796

2797
  if (path[0] != '~') {
336✔
2798
    sstrncpy(buf, path, buflen);
328✔
2799
    return 1;
328✔
2800
  }
2801

2802
  memset(user, '\0', sizeof(user));
8✔
2803

2804
  /* The first character of the given path is '~'.
2805
   *
2806
   * We next need to see what the rest of the path looks like.  Could be:
2807
   *
2808
   *  "~"
2809
   *  "~user"
2810
   *  "~/"
2811
   *  "~/path"
2812
   *  "~user/path"
2813
   */
2814

2815
  pathlen = strlen(path);
8✔
2816
  if (pathlen == 1) {
8✔
2817
    /* If the path is just "~", AND we're chrooted, then the interpolation
2818
     * is easy.
2819
     */
2820
    if (session.chroot_path != NULL) {
3✔
2821
      sstrncpy(buf, session.chroot_path, buflen);
1✔
2822
      return 1;
1✔
2823
    }
2824

2825
    /* If we are not chrooted, but we DO know the home directory of the
2826
     * current user, then interpolation is easy.
2827
     */
2828
    if (session.user_homedir != NULL) {
2✔
2829
      sstrncpy(buf, session.user_homedir, buflen);
1✔
2830
      return 1;
1✔
2831
    }
2832
  }
2833

2834
  ptr = strchr(path, '/');
6✔
2835
  if (ptr == NULL) {
6✔
2836
    struct stat st;
2✔
2837

2838
    /* No path separator present, which means path must be "~user".
2839
     *
2840
     * This means that a path of "~foo" could be a file with that exact
2841
     * name, or it could be that user's home directory.  Let's find out
2842
     * which it is.
2843
     */
2844

2845
    if (pr_fsio_stat(path, &st) < 0) {
2✔
2846
       /* Must be a user, if anything...otherwise it's probably a typo.
2847
        *
2848
        * The user name, then, is everything just past the '~' character.
2849
        */
2850
      sstrncpy(user, path+1,
2✔
2851
        pathlen-1 > sizeof(user)-1 ? sizeof(user)-1 : pathlen-1);
2✔
2852

2853
    } else {
2854
      /* This IS the file in question, perform no interpolation. */
2855
      return 0;
×
2856
    }
2857

2858
  } else {
2859
    currlen = ptr - path;
4✔
2860
    if (currlen > 1) {
4✔
2861
      /* Copy over the username. */
2862
      sstrncpy(user, path+1,
1✔
2863
        currlen > sizeof(user)-1 ? sizeof(user)-1 : currlen);
1✔
2864
    }
2865

2866
    /* Advance past the '/'. */
2867
    ptr++;
4✔
2868
  }
2869

2870
  if (user[0] == '\0') {
6✔
2871
    /* No user name provided.  If we are chrooted, we leave it that way.
2872
     * Otherwise, we're not chrooted, and we can assume the current user.
2873
     */
2874
    if (session.chroot_path == NULL) {
4✔
2875
      sstrncpy(user, session.user, sizeof(user)-1);
4✔
2876
    }
2877
  }
2878

2879
  if (user[0] != '\0') {
6✔
2880
    if (session.user != NULL &&
4✔
2881
        strcmp(user, session.user) == 0 &&
2✔
2882
        session.user_homedir != NULL) {
2✔
2883
      sstrncpy(buf, session.user_homedir, buflen);
1✔
2884

2885
    } else {
2886
      struct passwd *pw = NULL;
3✔
2887
      pool *p = NULL;
3✔
2888

2889
      /* We need to look up the info for the given username, and add it
2890
       * into the buffer.
2891
       *
2892
       * The permanent pool is used here, rather than session.pool, as path
2893
       * interpolation can occur during startup parsing, when session.pool does
2894
       * not exist.  It does not really matter, since the allocated sub pool
2895
       * is destroyed shortly.
2896
       */
2897
      p = make_sub_pool(permanent_pool);
3✔
2898
      pr_pool_tag(p, "pr_fs_interpolate() pool");
3✔
2899

2900
      pw = pr_auth_getpwnam(p, user);
3✔
2901
      if (pw == NULL) {
3✔
2902
        destroy_pool(p);
3✔
2903
        errno = ENOENT;
3✔
2904
        return -1;
3✔
2905
      }
2906

2907
      sstrncpy(buf, pw->pw_dir, buflen);
×
2908

2909
      /* Done with pw, which means we can destroy the temporary pool now. */
2910
      destroy_pool(p);
×
2911
    }
2912

2913
  } else {
2914
    /* We're chrooted. */
2915
    sstrncpy(buf, "/", buflen);
2✔
2916
  }
2917

2918
  currlen = strlen(buf);
3✔
2919

2920
  if (ptr != NULL &&
3✔
2921
      currlen < buflen &&
3✔
2922
      buf[currlen-1] != '/') {
2✔
2923
    buf[currlen++] = '/';
1✔
2924
  }
2925

2926
  if (ptr != NULL) {
3✔
2927
    sstrncpy(&buf[currlen], ptr, buflen - currlen);
2✔
2928
  }
2929

2930
  return 1;
2931
}
2932

2933
int pr_fs_resolve_partial(const char *path, char *buf, size_t buflen, int op) {
39✔
2934
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
39✔
2935
       workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'},
39✔
2936
       namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
39✔
2937
       *where = NULL, *ptr = NULL, *last = NULL;
39✔
2938
  pr_fs_t *fs = NULL;
39✔
2939
  int len = 0, fini = 1, link_cnt = 0;
39✔
2940
  ino_t prev_inode = 0;
39✔
2941
  dev_t prev_device = 0;
39✔
2942
  struct stat sbuf;
39✔
2943

2944
  if (path == NULL ||
39✔
2945
      buf == NULL ||
39✔
2946
      buflen == 0) {
2947
    errno = EINVAL;
3✔
2948
    return -1;
3✔
2949
  }
2950

2951
  if (*path != '/') {
36✔
2952
    if (*path == '~') {
2✔
2953
      switch (pr_fs_interpolate(path, curpath, sizeof(curpath)-1)) {
1✔
2954
        case -1:
2955
          return -1;
2956

2957
        case 0:
×
2958
          sstrncpy(curpath, path, sizeof(curpath));
×
2959
          sstrncpy(workpath, cwd, sizeof(workpath));
×
2960
          break;
×
2961
      }
2962

2963
    } else {
2964
      sstrncpy(curpath, path, sizeof(curpath));
1✔
2965
      sstrncpy(workpath, cwd, sizeof(workpath));
1✔
2966
    }
2967

2968
  } else {
2969
    sstrncpy(curpath, path, sizeof(curpath));
34✔
2970
  }
2971

2972
  while (fini--) {
64✔
2973
    where = curpath;
2974

2975
    while (*where != '\0') {
139✔
2976
      pr_signals_handle();
111✔
2977

2978
      /* Handle "." */
2979
      if (strncmp(where, ".", 2) == 0) {
111✔
2980
        where++;
1✔
2981
        continue;
1✔
2982
      }
2983

2984
      /* Handle ".." */
2985
      if (strncmp(where, "..", 3) == 0) {
110✔
2986
        where += 2;
×
2987
        ptr = last = workpath;
×
2988

2989
        while (*ptr) {
×
2990
          if (*ptr == '/') {
×
2991
            last = ptr;
×
2992
          }
2993
          ptr++;
×
2994
        }
2995

2996
        *last = '\0';
×
2997
        continue;
×
2998
      }
2999

3000
      /* Handle "./" */
3001
      if (strncmp(where, "./", 2) == 0) {
110✔
3002
        where += 2;
8✔
3003
        continue;
8✔
3004
      }
3005

3006
      /* Handle "../" */
3007
      if (strncmp(where, "../", 3) == 0) {
102✔
3008
        where += 3;
10✔
3009
        ptr = last = workpath;
10✔
3010

3011
        while (*ptr) {
67✔
3012
          if (*ptr == '/') {
57✔
3013
            last = ptr;
12✔
3014
          }
3015
          ptr++;
57✔
3016
        }
3017

3018
        *last = '\0';
10✔
3019
        continue;
10✔
3020
      }
3021

3022
      ptr = strchr(where, '/');
92✔
3023
      if (ptr == NULL) {
92✔
3024
        size_t wherelen = strlen(where);
24✔
3025

3026
        ptr = where;
24✔
3027
        if (wherelen >= 1) {
24✔
3028
          ptr += (wherelen - 1);
24✔
3029
        }
3030

3031
      } else {
3032
        *ptr = '\0';
68✔
3033
      }
3034

3035
      sstrncpy(namebuf, workpath, sizeof(namebuf));
92✔
3036

3037
      if (*namebuf) {
92✔
3038
        for (last = namebuf; *last; last++) {
214✔
3039
        }
55✔
3040

20✔
3041
        if (*--last != '/') {
3042
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3043
        }
3044

37✔
3045
      } else {
3046
        sstrcat(namebuf, "/", sizeof(namebuf)-1);
3047
      }
92✔
3048

3049
      sstrcat(namebuf, where, sizeof(namebuf)-1);
92✔
3050

3051
      where = ++ptr;
92✔
3052

3053
      fs = lookup_dir_fs(namebuf, op);
92✔
3054

3055
      if (fs_cache_lstat(fs, namebuf, &sbuf) == -1) {
3056
        return -1;
3057
      }
85✔
3058

1✔
3059
      if (S_ISLNK(sbuf.st_mode)) {
3060
        char linkpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3061

1✔
3062
        /* Detect an obvious recursive symlink */
×
3063
        if (sbuf.st_ino && (ino_t) sbuf.st_ino == prev_inode &&
×
3064
            sbuf.st_dev && (dev_t) sbuf.st_dev == prev_device) {
×
3065
          errno = ELOOP;
3066
          return -1;
3067
        }
1✔
3068

1✔
3069
        prev_inode = (ino_t) sbuf.st_ino;
3070
        prev_device = (dev_t) sbuf.st_dev;
1✔
3071

×
3072
        if (++link_cnt > PR_FSIO_MAX_LINK_COUNT) {
×
3073
          errno = ELOOP;
3074
          return -1;
3075
        }
1✔
3076
        
1✔
3077
        len = pr_fsio_readlink(namebuf, linkpath, sizeof(linkpath)-1);
×
3078
        if (len <= 0) {
×
3079
          errno = ENOENT;
3080
          return -1;
3081
        }
1✔
3082

1✔
3083
        *(linkpath + len) = '\0';
1✔
3084
        if (*linkpath == '/') {
3085
          *workpath = '\0';
3086
        }
3087

1✔
3088
        /* Trim any trailing slash. */
×
3089
        if (linkpath[len-1] == '/') {
3090
          linkpath[len-1] = '\0';
3091
        }
1✔
3092

×
3093
        if (*linkpath == '~') {
3094
          char tmpbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
×
3095

×
3096
          *workpath = '\0';
3097
          sstrncpy(tmpbuf, linkpath, sizeof(tmpbuf));
×
3098

×
3099
          if (pr_fs_interpolate(tmpbuf, linkpath, sizeof(linkpath)-1) < 0) {
3100
            return -1;
3101
          }
3102
        }
1✔
3103

×
3104
        if (*where) {
×
3105
          sstrcat(linkpath, "/", sizeof(linkpath)-1);
3106
          sstrcat(linkpath, where, sizeof(linkpath)-1);
3107
        }
1✔
3108

1✔
3109
        sstrncpy(curpath, linkpath, sizeof(curpath));
1✔
3110
        fini++;
3111
        break; /* continue main loop */
3112
      }
84✔
3113

75✔
3114
      if (S_ISDIR(sbuf.st_mode)) {
75✔
3115
        sstrncpy(workpath, namebuf, sizeof(workpath));
3116
        continue;
3117
      }
9✔
3118

×
3119
      if (*where) {
×
3120
        errno = ENOENT;
3121
        return -1;               /* path/notadir/morepath */
3122
      }
9✔
3123

3124
      sstrncpy(workpath, namebuf, sizeof(workpath));
3125
    }
3126
  }
28✔
3127

1✔
3128
  if (!workpath[0]) {
3129
    sstrncpy(workpath, "/", sizeof(workpath));
3130
  }
28✔
3131

28✔
3132
  sstrncpy(buf, workpath, buflen);
3133
  return 0;
3134
}
289✔
3135

289✔
3136
int pr_fs_resolve_path(const char *path, char *buf, size_t buflen, int op) {
289✔
3137
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
289✔
3138
       workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'},
289✔
3139
       namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
289✔
3140
       *where = NULL, *ptr = NULL, *last = NULL;
289✔
3141
  pr_fs_t *fs = NULL;
289✔
3142
  int len = 0, fini = 1, link_cnt = 0;
289✔
3143
  ino_t prev_inode = 0;
289✔
3144
  dev_t prev_device = 0;
3145
  struct stat sbuf;
289✔
3146

289✔
3147
  if (path == NULL ||
3148
      buf == NULL ||
3✔
3149
      buflen == 0) {
3✔
3150
    errno = EINVAL;
3151
    return -1;
3152
  }
286✔
3153

286✔
3154
  if (pr_fs_interpolate(path, curpath, sizeof(curpath)-1) != -1) {
3155
    sstrncpy(curpath, path, sizeof(curpath));
3156
  }
286✔
3157

×
3158
  if (curpath[0] != '/') {
3159
    sstrncpy(workpath, cwd, sizeof(workpath));
3160

286✔
3161
  } else {
3162
    workpath[0] = '\0';
3163
  }
561✔
3164

3165
  while (fini--) {
3166
    where = curpath;
1,699✔
3167

1,425✔
3168
    while (*where != '\0') {
3169
      pr_signals_handle();
1,425✔
3170

×
3171
      if (strncmp(where, ".", 2) == 0) {
×
3172
        where++;
3173
        continue;
3174
      }
3175

1,425✔
3176
      /* handle "./" */
×
3177
      if (strncmp(where, "./", 2) == 0) {
×
3178
        where += 2;
3179
        continue;
3180
      }
3181

1,425✔
3182
      /* handle "../" */
×
3183
      if (strncmp(where, "../", 3) == 0) {
×
3184
        where += 3;
×
3185
        ptr = last = workpath;
×
3186
        while (*ptr) {
×
3187
          if (*ptr == '/') {
3188
            last = ptr;
×
3189
          }
3190
          ptr++;
3191
        }
×
3192

×
3193
        *last = '\0';
3194
        continue;
3195
      }
1,425✔
3196

1,425✔
3197
      ptr = strchr(where, '/');
281✔
3198
      if (ptr == NULL) {
3199
        size_t wherelen = strlen(where);
281✔
3200

281✔
3201
        ptr = where;
281✔
3202
        if (wherelen >= 1) {
3203
          ptr += (wherelen - 1);
3204
        }
3205

1,144✔
3206
      } else {
3207
        *ptr = '\0';
3208
      }
1,425✔
3209

3210
      sstrncpy(namebuf, workpath, sizeof(namebuf));
1,425✔
3211

11,794✔
3212
      if (*namebuf) {
10,656✔
3213
        for (last = namebuf; *last; last++) {
1,138✔
3214
        }
852✔
3215
        if (*--last != '/') {
3216
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3217
        }
3218

287✔
3219
      } else {
3220
        sstrcat(namebuf, "/", sizeof(namebuf)-1);
3221
      }
1,425✔
3222

3223
      sstrcat(namebuf, where, sizeof(namebuf)-1);
1,425✔
3224

3225
      where = ++ptr;
1,425✔
3226

1,425✔
3227
      fs = lookup_dir_fs(namebuf, op);
12✔
3228
      if (fs_cache_lstat(fs, namebuf, &sbuf) == -1) {
12✔
3229
        errno = ENOENT;
3230
        return -1;
3231
      }
1,413✔
3232

1✔
3233
      if (S_ISLNK(sbuf.st_mode)) {
3234
        char linkpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3235

1✔
3236
        /* Detect an obvious recursive symlink */
×
3237
        if (sbuf.st_ino && (ino_t) sbuf.st_ino == prev_inode &&
×
3238
            sbuf.st_dev && (dev_t) sbuf.st_dev == prev_device) {
×
3239
          errno = ELOOP;
3240
          return -1;
3241
        }
1✔
3242

1✔
3243
        prev_inode = (ino_t) sbuf.st_ino;
3244
        prev_device = (dev_t) sbuf.st_dev;
1✔
3245

×
3246
        if (++link_cnt > PR_FSIO_MAX_LINK_COUNT) {
×
3247
          errno = ELOOP;
3248
          return -1;
3249
        }
1✔
3250

1✔
3251
        len = pr_fsio_readlink(namebuf, linkpath, sizeof(linkpath)-1);
×
3252
        if (len <= 0) {
×
3253
          errno = ENOENT;
3254
          return -1;
3255
        }
1✔
3256

3257
        *(linkpath+len) = '\0';
1✔
3258

1✔
3259
        if (*linkpath == '/') {
3260
          *workpath = '\0';
3261
        }
3262

1✔
3263
        /* Trim any trailing slash. */
×
3264
        if (linkpath[len-1] == '/') {
3265
          linkpath[len-1] = '\0';
3266
        }
1✔
3267

×
3268
        if (*linkpath == '~') {
×
3269
          char tmpbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3270
          *workpath = '\0';
×
3271

3272
          sstrncpy(tmpbuf, linkpath, sizeof(tmpbuf));
×
3273

×
3274
          if (pr_fs_interpolate(tmpbuf, linkpath, sizeof(linkpath)-1) < 0) {
3275
            return -1;
3276
          }
3277
        }
1✔
3278

×
3279
        if (*where) {
×
3280
          sstrcat(linkpath, "/", sizeof(linkpath)-1);
3281
          sstrcat(linkpath, where, sizeof(linkpath)-1);
3282
        }
1✔
3283

1✔
3284
        sstrncpy(curpath, linkpath, sizeof(curpath));
1✔
3285
        fini++;
3286
        break; /* continue main loop */
3287
      }
1,412✔
3288

1,412✔
3289
      if (S_ISDIR(sbuf.st_mode)) {
1,412✔
3290
        sstrncpy(workpath, namebuf, sizeof(workpath));
3291
        continue;
3292
      }
×
3293

×
3294
      if (*where) {
×
3295
        errno = ENOENT;
3296
        return -1;               /* path/notadir/morepath */
3297
      }
×
3298

3299
      sstrncpy(workpath, namebuf, sizeof(workpath));
3300
    }
3301
  }
274✔
3302

×
3303
  if (!workpath[0]) {
3304
    sstrncpy(workpath, "/", sizeof(workpath));
3305
  }
274✔
3306

274✔
3307
  sstrncpy(buf, workpath, buflen);
3308
  return 0;
3309
}
1,724✔
3310

1,724✔
3311
int pr_fs_clean_path2(const char *path, char *buf, size_t buflen, int flags) {
1,724✔
3312
  char workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
1,724✔
3313
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'};
1,724✔
3314
  char namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'};
3315
  int fini = 1, have_abs_path = FALSE;
1,724✔
3316

1,724✔
3317
  if (path == NULL ||
4✔
3318
      buf == NULL) {
4✔
3319
    errno = EINVAL;
3320
    return -1;
3321
  }
1,720✔
3322

3323
  if (buflen == 0) {
3324
    return 0;
3325
  }
1,718✔
3326

3327
  sstrncpy(curpath, path, sizeof(curpath));
1,718✔
3328

1,713✔
3329
  if (*curpath == '/') {
3330
    have_abs_path = TRUE;
3331
  }
3332

3,436✔
3333
  /* main loop */
6,795✔
3334
  while (fini--) {
3335
    char *where = NULL, *ptr = NULL, *last = NULL;
3336

6,795✔
3337
    where = curpath;
5,077✔
3338
    while (*where != '\0') {
3339
      pr_signals_handle();
5,077✔
3340

1✔
3341
      if (strncmp(where, ".", 2) == 0) {
1✔
3342
        where++;
3343
        continue;
3344
      }
3345

5,076✔
3346
      /* handle "./" */
12✔
3347
      if (strncmp(where, "./", 2) == 0) {
12✔
3348
        where += 2;
3349
        continue;
3350
      }
3351

5,064✔
3352
      /* handle ".." */
×
3353
      if (strncmp(where, "..", 3) == 0) {
×
3354
        where += 2;
3355
        ptr = last = workpath;
×
3356

×
3357
        while (*ptr) {
3358
          pr_signals_handle();
×
3359

×
3360
          if (*ptr == '/') {
3361
            last = ptr;
3362
          }
×
3363

3364
          ptr++;
3365
        }
×
3366

×
3367
        *last = '\0';
3368
        continue;
3369
      }
3370

5,064✔
3371
      /* handle "../" */
3✔
3372
      if (strncmp(where, "../", 3) == 0) {
3✔
3373
        where += 3;
3374
        ptr = last = workpath;
21✔
3375

18✔
3376
        while (*ptr) {
3377
          pr_signals_handle();
18✔
3378

4✔
3379
          if (*ptr == '/') {
3380
            last = ptr;
18✔
3381
          }
3382
          ptr++;
3383
        }
3✔
3384

3✔
3385
        *last = '\0';
3386
        continue;
3387
      }
5,061✔
3388

5,061✔
3389
      ptr = strchr(where, '/');
1,367✔
3390
      if (ptr == NULL) {
3391
        size_t wherelen = strlen(where);
1,367✔
3392

1,367✔
3393
        ptr = where;
1,367✔
3394
        if (wherelen >= 1) {
3395
          ptr += (wherelen - 1);
3396
        }
3397

3,694✔
3398
      } else {
3399
        *ptr = '\0';
3400
      }
5,061✔
3401

3402
      sstrncpy(namebuf, workpath, sizeof(namebuf));
5,061✔
3403

23,228✔
3404
      if (*namebuf) {
19,886✔
3405
        for (last = namebuf; *last; last++) {
3,342✔
3406
        }
1,962✔
3407
        if (*--last != '/') {
3408
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3409
        }
3410

1,719✔
3411
      } else {
6✔
3412
        if (have_abs_path ||
1,717✔
3413
            (flags & PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH)) {
1,717✔
3414
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3415
          have_abs_path = FALSE;
3416
        }
3417
      }
5,061✔
3418

5,061✔
3419
      sstrcat(namebuf, where, sizeof(namebuf)-1);
3420
      namebuf[sizeof(namebuf)-1] = '\0';
5,061✔
3421

3422
      where = ++ptr;
5,061✔
3423

3424
      sstrncpy(workpath, namebuf, sizeof(workpath));
3425
    }
3426
  }
1,718✔
3427

1✔
3428
  if (!workpath[0]) {
3429
    sstrncpy(workpath, "/", sizeof(workpath));
3430
  }
1,718✔
3431

1,718✔
3432
  sstrncpy(buf, workpath, buflen);
3433
  return 0;
3434
}
38✔
3435

35✔
3436
void pr_fs_clean_path(const char *path, char *buf, size_t buflen) {
17✔
3437
  pr_fs_clean_path2(path, buf, buflen, PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH);
3438
}
4✔
3439

4✔
3440
int pr_fs_use_encoding(int do_encode) {
3441
  int curr_setting = use_encoding;
4✔
3442

3443
  if (do_encode != TRUE &&
1✔
3444
      do_encode != FALSE) {
1✔
3445
    errno = EINVAL;
3446
    return -1;
3447
  }
3✔
3448

3✔
3449
  use_encoding = do_encode;
3450
  return curr_setting;
3451
}
75✔
3452

3453
char *pr_fs_decode_path2(pool *p, const char *path, int flags) {
75✔
3454
#if defined(PR_USE_NLS)
75✔
3455
  size_t outlen;
3456
  char *res;
75✔
3457

75✔
3458
  if (p == NULL ||
5✔
3459
      path == NULL) {
5✔
3460
    errno = EINVAL;
3461
    return NULL;
3462
  }
70✔
3463

3464
  if (use_encoding == FALSE) {
3465
    return (char *) path;
3466
  }
70✔
3467

70✔
3468
  res = pr_decode_str(p, path, strlen(path), &outlen);
×
3469
  if (res == NULL) {
3470
    int xerrno = errno;
×
3471

3472
    pr_trace_msg("encode", 1, "error decoding path '%s': %s", path,
3473
      strerror(xerrno));
×
3474

3475
    if (pr_trace_get_level("encode") >= 14) {
×
3476
      /* Write out the path we tried (and failed) to decode, in hex. */
×
3477
      register unsigned int i;
×
3478
      unsigned char *raw_path;
3479
      size_t pathlen, raw_pathlen;
×
3480

×
3481
      pathlen = strlen(path);
×
3482
      raw_pathlen = (pathlen * 8) + 1;
3483
      raw_path = pcalloc(p, raw_pathlen + 1);
×
3484

×
3485
      for (i = 0; i < pathlen; i++) {
×
3486
        pr_snprintf((char *) (raw_path + (i * 8)), (raw_pathlen - 1) - (i * 8),
3487
          "0x%02x ", (unsigned char) path[i]);
3488
      }
×
3489

3490
      pr_trace_msg("encode", 14, "unable to decode path (raw bytes): %s",
3491
        raw_path);
3492
    }
×
3493

×
3494
    if (flags & FSIO_DECODE_FL_TELL_ERRORS) {
3495
      unsigned long policy;
×
3496

×
3497
      policy = pr_encode_get_policy();
3498
      if (policy & PR_ENCODE_POLICY_FL_REQUIRE_VALID_ENCODING) {
3499
        /* Note: At present, we DO return null here to callers, to indicate
3500
         * the illegal encoding (Bug#4125), if configured to do so via
3501
         * e.g. the RequireValidEncoding LangOption.
×
3502
         */
×
3503
        errno = xerrno;
3504
        return NULL;
3505
      }
3506
    }
×
3507

3508
    return (char *) path;
3509
  }
70✔
3510

70✔
3511
  pr_trace_msg("encode", 5, "decoded '%s' into '%s'", path, res);
3512
  return res;
3513
#else
3514
  if (p == NULL ||
3515
      path == NULL) {
3516
    errno = EINVAL;
3517
    return NULL;
3518
  }
3519

3520
  return (char *) path;
3521
#endif /* PR_USE_NLS */
3522
}
72✔
3523

72✔
3524
char *pr_fs_decode_path(pool *p, const char *path) {
3525
  return pr_fs_decode_path2(p, path, 0);
3526
}
4✔
3527

3528
char *pr_fs_encode_path(pool *p, const char *path) {
4✔
3529
#ifdef PR_USE_NLS
4✔
3530
  size_t outlen;
3531
  char *res;
4✔
3532

4✔
3533
  if (p == NULL ||
2✔
3534
      path == NULL) {
2✔
3535
    errno = EINVAL;
3536
    return NULL;
3537
  }
2✔
3538

3539
  if (!use_encoding) {
3540
    return (char *) path;
3541
  }
2✔
3542

2✔
3543
  res = pr_encode_str(p, path, strlen(path), &outlen);
×
3544
  if (res == NULL) {
3545
    int xerrno = errno;
×
3546

3547
    pr_trace_msg("encode", 1, "error encoding path '%s': %s", path,
3548
      strerror(xerrno));
×
3549

3550
    if (pr_trace_get_level("encode") >= 14) {
×
3551
      /* Write out the path we tried (and failed) to encode, in hex. */
×
3552
      register unsigned int i;
×
3553
      unsigned char *raw_path;
3554
      size_t pathlen, raw_pathlen;
×
3555

×
3556
      pathlen = strlen(path);
×
3557
      raw_pathlen = (pathlen * 8) + 1;
3558
      raw_path = pcalloc(p, raw_pathlen + 1);
×
3559

×
3560
      for (i = 0; i < pathlen; i++) {
×
3561
        pr_snprintf((char *) (raw_path + (i * 8)), (raw_pathlen - 1) - (i * 8),
3562
          "0x%02x ", (unsigned char) path[i]);
3563
      }
×
3564

3565
      pr_trace_msg("encode", 14, "unable to encode path (raw bytes): %s",
3566
        raw_path);
3567
    }
3568

3569
    /* Note: At present, we do NOT return null here to callers; we assume
3570
     * that all local names, being encoded for the remote client, are OK.
3571
     * Revisit this assumption if necessary (Bug#4125).
3572
     */
×
3573

3574
    return (char *) path;
3575
  }
2✔
3576

2✔
3577
  pr_trace_msg("encode", 5, "encoded '%s' into '%s'", path, res);
3578
  return res;
3579
#else
3580
  if (p == NULL ||
3581
      path == NULL) {
3582
    errno = EINVAL;
3583
    return NULL;
3584
  }
3585

3586
  return (char *) path;
3587
#endif /* PR_USE_NLS */
3588
}
10✔
3589

10✔
3590
array_header *pr_fs_split_path(pool *p, const char *path) {
10✔
3591
  int res, have_abs_path = FALSE;
10✔
3592
  char *buf;
10✔
3593
  size_t buflen, bufsz, pathlen;
3594
  array_header *components;
10✔
3595

10✔
3596
  if (p == NULL ||
2✔
3597
      path == NULL) {
2✔
3598
    errno = EINVAL;
3599
    return NULL;
3600
  }
8✔
3601

8✔
3602
  pathlen = strlen(path);
1✔
3603
  if (pathlen == 0) {
1✔
3604
    errno = EINVAL;
3605
    return NULL;
3606
  }
7✔
3607

5✔
3608
  if (*path == '/') {
3609
    have_abs_path = TRUE;
3610
  }
3611

7✔
3612
  /* Clean the path first */
7✔
3613
  bufsz = PR_TUNABLE_PATH_MAX;
3614
  buf = pcalloc(p, bufsz + 1);
7✔
3615

3616
  res = pr_fs_clean_path2(path, buf, bufsz,
7✔
3617
    PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH);
×
3618
  if (res < 0) {
3619
    int xerrno = errno;
×
3620

3621
    pr_trace_msg(trace_channel, 7, "error cleaning path '%s': %s", path,
×
3622
      strerror(xerrno));
×
3623
    errno = xerrno;
3624
    return NULL;
3625
  }
7✔
3626

3627
  pr_trace_msg(trace_channel, 18, "splitting cleaned path '%s' (was '%s')",
3628
    buf, path);
3629

3630
  /* Special-case handling of just "/", since pr_str_text_to_array() will
3631
   * "eat" that delimiter.
7✔
3632
   */
7✔
3633
  buflen = strlen(buf);
3✔
3634
  if (buflen == 1 &&
3✔
3635
      buf[0] == '/') {
3636
    pr_trace_msg(trace_channel, 18, "split path '%s' into 1 component", path);
3✔
3637

3✔
3638
    components = make_array(p, 1, sizeof(char *));
3639
    *((char **) push_array(components)) = pstrdup(p, "/");
3✔
3640

3641
    return components;
3642
  }
4✔
3643

4✔
3644
  components = pr_str_text_to_array(p, buf, '/');
4✔
3645
  if (components != NULL) {
4✔
3646
    pr_trace_msg(trace_channel, 17, "split path '%s' into %u %s", path,
3647
      components->nelts, components->nelts != 1 ? "components" : "component");
4✔
3648

3649
    if (pr_trace_get_level(trace_channel) >= 18) {
3650
      register unsigned int i;
16✔
3651

12✔
3652
      for (i = 0; i < components->nelts; i++) {
3653
        char *component;
12✔
3654

12✔
3655
        component = ((char **) components->elts)[i];
×
3656
        if (component == NULL) {
3657
          component = "NULL";
3658
        }
12✔
3659

3660
        pr_trace_msg(trace_channel, 18, "path '%s' component #%u: '%s'",
3661
          path, i + 1, component);
3662
      }
3663
    }
3664
  }
4✔
3665

3✔
3666
  if (have_abs_path == TRUE) {
3667
    array_header *root_component;
3668

3669
    /* Since pr_str_text_to_array() will treat the leading '/' as a delimiter,
3670
     * it will be stripped and not included as a path component.  But it
3671
     * DOES need to be there.
3✔
3672
     */
3✔
3673
    root_component = make_array(p, 1, sizeof(char *));
3674
    *((char **) push_array(root_component)) = pstrdup(p, "/");
3✔
3675

3✔
3676
    array_cat(root_component, components);
3677
    components = root_component;
3678
  }
3679

3680
  return components;
3681
}
9✔
3682

9✔
3683
char *pr_fs_join_path(pool *p, array_header *components, size_t count) {
9✔
3684
  register unsigned int i;
3685
  char *path = NULL;
9✔
3686

9✔
3687
  if (p == NULL ||
7✔
3688
      components == NULL ||
3689
      components->nelts == 0 ||
4✔
3690
      count == 0) {
4✔
3691
    errno = EINVAL;
3692
    return NULL;
3693
  }
3694

5✔
3695
  /* Can't join more components than we have. */
1✔
3696
  if (count > components->nelts) {
1✔
3697
    errno = EINVAL;
3698
    return NULL;
3699
  }
4✔
3700

4✔
3701
  path = ((char **) components->elts)[0];
3702
  path = pstrdup(p, path);
15✔
3703

7✔
3704
  for (i = 1; i < count; i++) {
3705
    char *elt;
7✔
3706

7✔
3707
    elt = ((char **) components->elts)[i];
3708
    path = pdircat(p, path, elt, NULL);
3709
  }
3710

3711
  return path;
3712
}
3713

3714
/* This function checks the given path's prefix against the paths that
3715
 * have been registered.  If no matching path prefix has been registered,
3716
 * the path is considered invalid.
2,220✔
3717
 */
2,220✔
3718
int pr_fs_valid_path(const char *path) {
1✔
3719
  if (path == NULL) {
1✔
3720
    errno = EINVAL;
3721
    return -1;
3722
  }
2,219✔
3723

6✔
3724
  if (fs_map != NULL &&
6✔
3725
      fs_map->nelts > 0) {
6✔
3726
    pr_fs_t *fsi = NULL, **fs_objs = (pr_fs_t **) fs_map->elts;
3727
    register unsigned int i;
10✔
3728

7✔
3729
    for (i = 0; i < fs_map->nelts; i++) {
3730
      fsi = fs_objs[i];
7✔
3731

3732
      if (strncmp(fsi->fs_path, path, strlen(fsi->fs_path)) == 0) {
3733
        return 0;
3734
      }
3735
    }
3736
  }
3737

2,216✔
3738
  /* Also check the path against the default '/' path. */
3739
  if (*path == '/') {
3740
    return 0;
3741
  }
7✔
3742

7✔
3743
  errno = ENOENT;
3744
  return -1;
3745
}
7✔
3746

7✔
3747
void pr_fs_virtual_path(const char *path, char *buf, size_t buflen) {
7✔
3748
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
7✔
3749
       workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'},
7✔
3750
       namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
7✔
3751
       *where = NULL, *ptr = NULL, *last = NULL;
3752
  int fini = 1;
7✔
3753

1✔
3754
  if (path == NULL) {
3755
    return;
3756
  }
6✔
3757

6✔
3758
  if (pr_fs_interpolate(path, curpath, sizeof(curpath)-1) != -1) {
3759
    sstrncpy(curpath, path, sizeof(curpath));
3760
  }
6✔
3761

2✔
3762
  if (curpath[0] != '/') {
3763
    sstrncpy(workpath, vwd, sizeof(workpath));
3764

4✔
3765
  } else {
3766
    workpath[0] = '\0';
3767
  }
3768

3769
  /* curpath is path resolving */
3770
  /* linkpath is path a symlink pointed to */
3771
  /* workpath is the path we've resolved */
3772

12✔
3773
  /* main loop */
3774
  while (fini--) {
3775
    where = curpath;
20✔
3776

14✔
3777
    while (*where != '\0') {
3778
      pr_signals_handle();
14✔
3779

×
3780
      if (strncmp(where, ".", 2) == 0) {
×
3781
        where++;
3782
        continue;
3783
      }
3784

14✔
3785
      /* handle "./" */
2✔
3786
      if (strncmp(where, "./", 2) == 0) {
2✔
3787
        where += 2;
3788
        continue;
3789
      }
3790

12✔
3791
      /* handle ".." */
×
3792
      if (strncmp(where, "..", 3) == 0) {
×
3793
        where += 2;
×
3794
        ptr = last = workpath;
×
3795
        while (*ptr) {
×
3796
          if (*ptr == '/') {
3797
            last = ptr;
×
3798
          }
3799
          ptr++;
3800
        }
×
3801

×
3802
        *last = '\0';
3803
        continue;
3804
      }
3805

12✔
3806
      /* handle "../" */
2✔
3807
      if (strncmp(where, "../", 3) == 0) {
2✔
3808
        where += 3;
6✔
3809
        ptr = last = workpath;
4✔
3810
        while (*ptr) {
1✔
3811
          if (*ptr == '/') {
3812
            last = ptr;
4✔
3813
          }
3814
          ptr++;
3815
        }
2✔
3816

2✔
3817
        *last = '\0';
3818
        continue;
3819
      }
10✔
3820

10✔
3821
      ptr = strchr(where, '/');
4✔
3822
      if (ptr == NULL) {
3823
        size_t wherelen = strlen(where);
4✔
3824

4✔
3825
        ptr = where;
4✔
3826
        if (wherelen >= 1) {
3827
          ptr += (wherelen - 1);
3828
        }
3829

6✔
3830
      } else {
3831
        *ptr = '\0';
3832
      }
10✔
3833

3834
      sstrncpy(namebuf, workpath, sizeof(namebuf));
10✔
3835

12✔
3836
      if (*namebuf) {
6✔
3837
        for (last = namebuf; *last; last++) {
6✔
3838
        }
×
3839
        if (*--last != '/') {
3840
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3841
        }
3842

4✔
3843
      } else {
3844
        sstrcat(namebuf, "/", sizeof(namebuf)-1);
3845
      }
10✔
3846

3847
      sstrcat(namebuf, where, sizeof(namebuf)-1);
10✔
3848

3849
      where = ++ptr;
10✔
3850

3851
      sstrncpy(workpath, namebuf, sizeof(workpath));
3852
    }
3853
  }
6✔
3854

1✔
3855
  if (!workpath[0]) {
3856
    sstrncpy(workpath, "/", sizeof(workpath));
3857
  }
6✔
3858

3859
  sstrncpy(buf, workpath, buflen);
3860
}
3✔
3861

3✔
3862
int pr_fsio_chdir_canon(const char *path, int hidesymlink) {
3✔
3863
  char resbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3✔
3864
  pr_fs_t *fs = NULL;
3865
  int res = 0;
3✔
3866

1✔
3867
  if (path == NULL) {
1✔
3868
    errno = EINVAL;
3869
    return -1;
3870
  }
2✔
3871

3872
  if (pr_fs_resolve_partial(path, resbuf, sizeof(resbuf)-1,
3873
      FSIO_DIR_CHDIR) < 0) {
3874
    return -1;
3875
  }
2✔
3876

2✔
3877
  fs = lookup_dir_fs(resbuf, FSIO_DIR_CHDIR);
3878
  if (fs == NULL) {
3879
    return -1;
3880
  }
3881

3882
  /* Find the first non-NULL custom chdir handler.  If there are none,
3883
   * use the system chdir.
2✔
3884
   */
3885
  while (fs && fs->fs_next && !fs->chdir) {
3886
    fs = fs->fs_next;
3887
  }
2✔
3888

3889
  pr_trace_msg(trace_channel, 8, "using %s chdir() for path '%s'", fs->fs_name,
2✔
3890
    path);
2✔
3891
  res = (fs->chdir)(fs, resbuf);
3892
  if (res == 0) {
2✔
3893
    /* chdir succeeded, so we set fs_cwd for future references. */
3894
     fs_cwd = fs;
2✔
3895

×
3896
     if (hidesymlink) {
3897
       pr_fs_virtual_path(path, vwd, sizeof(vwd)-1);
3898

2✔
3899
     } else {
3900
       sstrncpy(vwd, resbuf, sizeof(vwd));
3901
     }
3902
  }
3903

3904
  return res;
3905
}
4✔
3906

4✔
3907
int pr_fsio_chdir(const char *path, int hidesymlink) {
4✔
3908
  char resbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
4✔
3909
  pr_fs_t *fs = NULL;
3910
  int res;
4✔
3911

1✔
3912
  if (path == NULL) {
1✔
3913
    errno = EINVAL;
3914
    return -1;
3915
  }
3✔
3916

3917
  pr_fs_clean_path(path, resbuf, sizeof(resbuf)-1);
3✔
3918

3✔
3919
  fs = lookup_dir_fs(path, FSIO_DIR_CHDIR);
3920
  if (fs == NULL) {
3921
    return -1;
3922
  }
3923

3924
  /* Find the first non-NULL custom chdir handler.  If there are none,
3925
   * use the system chdir.
3✔
3926
   */
3927
  while (fs && fs->fs_next && !fs->chdir) {
3928
    fs = fs->fs_next;
3929
  }
3✔
3930

3931
  pr_trace_msg(trace_channel, 8, "using %s chdir() for path '%s'", fs->fs_name,
3✔
3932
    path);
3✔
3933
  res = (fs->chdir)(fs, resbuf);
3934
  if (res == 0) {
2✔
3935
    /* chdir succeeded, so we set fs_cwd for future references. */
3936
    fs_cwd = fs;
2✔
3937

×
3938
    if (hidesymlink) {
3939
      pr_fs_virtual_path(path, vwd, sizeof(vwd)-1);
3940

2✔
3941
    } else {
3942
      sstrncpy(vwd, resbuf, sizeof(vwd));
3943
    }
3944
  }
3945

3946
  return res;
3947
}
3948

3949
/* fs_opendir, fs_closedir and fs_readdir all use a nifty
3950
 * optimization, caching the last-recently-used pr_fs_t, and
3951
 * avoid future pr_fs_t lookups when iterating via readdir.
14✔
3952
 */
14✔
3953
void *pr_fsio_opendir(const char *path) {
14✔
3954
  pr_fs_t *fs = NULL;
14✔
3955
  fsopendir_t *fsod = NULL, *fsodi = NULL;
14✔
3956
  pool *fsod_pool = NULL;
3957
  DIR *res = NULL;
14✔
3958

1✔
3959
  if (path == NULL) {
1✔
3960
    errno = EINVAL;
3961
    return NULL;
3962
  }
13✔
3963

1✔
3964
  if (strchr(path, '/') == NULL) {
1✔
3965
    pr_fs_setcwd(pr_fs_getcwd());
3966
    fs = fs_cwd;
3967

12✔
3968
  } else {
3969
    char buf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
12✔
3970

×
3971
    if (pr_fs_resolve_partial(path, buf, sizeof(buf)-1, FSIO_DIR_OPENDIR) < 0) {
3972
      return NULL;
3973
    }
12✔
3974

3975
    fs = lookup_dir_fs(buf, FSIO_DIR_OPENDIR);
3976
  }
3977

3978
  /* Find the first non-NULL custom opendir handler.  If there are none,
3979
   * use the system opendir.
13✔
3980
   */
3981
  while (fs && fs->fs_next && !fs->opendir) {
3982
    fs = fs->fs_next;
3983
  }
13✔
3984

3985
  pr_trace_msg(trace_channel, 8, "using %s opendir() for path '%s'",
13✔
3986
    fs->fs_name, path);
13✔
3987
  res = (fs->opendir)(fs, path);
3988
  if (res == NULL) {
3989
    return NULL;
3990
  }
3991

12✔
3992
  /* Cache it here */
12✔
3993
  fs_cache_dir = res;
3994
  fs_cache_fsdir = fs;
12✔
3995

12✔
3996
  fsod_pool = make_sub_pool(permanent_pool);
3997
  pr_pool_tag(fsod_pool, "fsod subpool");
12✔
3998

12✔
3999
  fsod = pcalloc(fsod_pool, sizeof(fsopendir_t));
×
4000
  if (fsod == NULL) {
×
4001
    if (fs->closedir) {
×
4002
      (fs->closedir)(fs, res);
×
4003
      errno = ENOMEM;
4004
      return NULL;
4005
    }
×
4006

×
4007
    sys_closedir(fs, res);
×
4008
    errno = ENOMEM;
4009
    return NULL;
4010
  }
12✔
4011

12✔
4012
  fsod->pool = fsod_pool;
12✔
4013
  fsod->dir = res;
12✔
4014
  fsod->fsdir = fs;
12✔
4015
  fsod->next = NULL;
4016
  fsod->prev = NULL;
12✔
4017

4018
  if (fsopendir_list) {
4019

4020
    /* find the end of the fsopendir list */
1✔
4021
    fsodi = fsopendir_list;
×
4022
    while (fsodi->next) {
×
4023
      pr_signals_handle();
4024
      fsodi = fsodi->next;
4025
    }
1✔
4026

1✔
4027
    fsod->next = NULL;
1✔
4028
    fsod->prev = fsodi;
4029
    fsodi->next = fsod;
4030

4031
  } else {
11✔
4032
    /* This fsopendir _becomes_ the start of the fsopendir list */
4033
    fsopendir_list = fsod;
4034
  }
4035

4036
  return res;
4037
}
79✔
4038

79✔
4039
static pr_fs_t *find_opendir(void *dir, int closing) {
4040
  pr_fs_t *fs = NULL;
79✔
4041

4042
  if (fsopendir_list != NULL) {
4043
    fsopendir_t *fsod;
77✔
4044

77✔
4045
    for (fsod = fsopendir_list; fsod; fsod = fsod->next) {
4046
      if (fsod->dir != NULL &&
77✔
4047
          fsod->dir == dir) {
77✔
4048
        fs = fsod->fsdir;
4049
        break;
4050
      }
4051
    }
77✔
4052

12✔
4053
    if (closing && fsod) {
×
4054
      if (fsod->prev != NULL) {
4055
        fsod->prev->next = fsod->next;
4056
      }
12✔
4057

1✔
4058
      if (fsod->next != NULL) {
4059
        fsod->next->prev = fsod->prev;
4060
      }
12✔
4061

12✔
4062
      if (fsod == fsopendir_list) {
4063
        fsopendir_list = fsod->next;
4064
      }
12✔
4065

4066
      destroy_pool(fsod->pool);
4067
    }
4068
  }
79✔
4069

76✔
4070
  if (dir == fs_cache_dir) {
4071
    fs = fs_cache_fsdir;
76✔
4072

11✔
4073
    if (closing) {
11✔
4074
      fs_cache_dir = NULL;
4075
      fs_cache_fsdir = NULL;
4076
    }
4077
  }
79✔
4078

2✔
4079
  if (fs == NULL) {
4080
    errno = ENOTDIR;
4081
  }
79✔
4082

4083
  return fs;
4084
}
14✔
4085

14✔
4086
int pr_fsio_closedir(void *dir) {
14✔
4087
  int res;
4088
  pr_fs_t *fs;
14✔
4089

1✔
4090
  if (dir == NULL) {
1✔
4091
    errno = EINVAL;
4092
    return -1;
4093
  }
13✔
4094

13✔
4095
  fs = find_opendir(dir, TRUE);
4096
  if (fs == NULL) {
4097
    return -1;
4098
  }
4099

4100
  /* Find the first non-NULL custom closedir handler.  If there are none,
4101
   * use the system closedir.
12✔
4102
   */
4103
  while (fs && fs->fs_next && !fs->closedir) {
4104
    fs = fs->fs_next;
4105
  }
12✔
4106

12✔
4107
  pr_trace_msg(trace_channel, 8, "using %s closedir()", fs->fs_name);
4108
  res = (fs->closedir)(fs, dir);
12✔
4109

4110
  return res;
4111
}
67✔
4112

67✔
4113
struct dirent *pr_fsio_readdir(void *dir) {
67✔
4114
  struct dirent *res;
4115
  pr_fs_t *fs;
67✔
4116

1✔
4117
  if (dir == NULL) {
1✔
4118
    errno = EINVAL;
4119
    return NULL;
4120
  }
66✔
4121

66✔
4122
  fs = find_opendir(dir, FALSE);
4123
  if (fs == NULL) {
4124
    return NULL;
4125
  }
4126

4127
  /* Find the first non-NULL custom readdir handler.  If there are none,
4128
   * use the system readdir.
65✔
4129
   */
4130
  while (fs && fs->fs_next && !fs->readdir) {
4131
    fs = fs->fs_next;
4132
  }
65✔
4133

65✔
4134
  pr_trace_msg(trace_channel, 8, "using %s readdir()", fs->fs_name);
4135
  res = (fs->readdir)(fs, dir);
65✔
4136

4137
  return res;
4138
}
9✔
4139

9✔
4140
int pr_fsio_mkdir(const char *path, mode_t mode) {
9✔
4141
  int res, xerrno;
9✔
4142
  pr_fs_t *fs;
4143
  mode_t dir_umask = -1, prev_umask = -1, *umask_ptr = NULL;
9✔
4144

1✔
4145
  if (path == NULL) {
1✔
4146
    errno = EINVAL;
4147
    return -1;
4148
  }
8✔
4149

8✔
4150
  fs = lookup_dir_fs(path, FSIO_DIR_MKDIR);
4151
  if (fs == NULL) {
4152
    return -1;
4153
  }
4154

4155
  /* Find the first non-NULL custom mkdir handler.  If there are none,
4156
   * use the system mkdir.
8✔
4157
   */
4158
  while (fs && fs->fs_next && !fs->mkdir) {
4159
    fs = fs->fs_next;
4160
  }
4161

8✔
4162
  /* Make sure we honor the directory Umask, if any (Bug#4311). */
8✔
4163
  umask_ptr = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
4164
  if (umask_ptr == NULL) {
4165
    /* If Umask was configured with a single parameter, then DirUmask
4166
     * would not be present; we still should check for Umask.
8✔
4167
     */
4168
    umask_ptr = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
4169
  }
8✔
4170

×
4171
  if (umask_ptr != NULL) {
4172
    dir_umask = *umask_ptr;
×
4173

×
4174
    if (dir_umask != (mode_t) -1) {
4175
      prev_umask = umask(dir_umask);
4176
    }
4177
  }
8✔
4178

4179
  pr_trace_msg(trace_channel, 8, "using %s mkdir() for path '%s'", fs->fs_name,
8✔
4180
    path);
8✔
4181
  res = (fs->mkdir)(fs, path, mode);
4182
  xerrno = errno;
8✔
4183

3✔
4184
  if (res == 0 || xerrno == EEXIST) {
4185
    pr_fs_clear_cache2(path);
4186
  }
8✔
4187

×
4188
  if (dir_umask != (mode_t) -1) {
4189
    (void) umask(prev_umask);
4190
  }
8✔
4191

8✔
4192
  errno = xerrno;
4193
  return res;
4194
}
3✔
4195

4196
int pr_fsio_mkdir_with_error(pool *p, const char *path, mode_t mode,
3✔
4197
    pr_error_t **err) {
4198
  int res;
3✔
4199

3✔
4200
  res = pr_fsio_mkdir(path, mode);
3✔
4201
  if (res < 0) {
4202
    int xerrno = errno;
3✔
4203

3✔
4204
    if (p != NULL &&
2✔
4205
        err != NULL) {
2✔
4206
      *err = pr_error_create(p, xerrno);
1✔
4207
      if (pr_error_explain_mkdir(*err, path, mode) < 0) {
1✔
4208
        pr_error_destroy(*err);
4209
        *err = NULL;
4210
      }
4211
    }
3✔
4212

4213
    errno = xerrno;
4214
  }
3✔
4215

4216
  return res;
4217
}
149✔
4218

149✔
4219
int pr_fsio_guard_chroot(int guard) {
4220
  int prev;
149✔
4221

149✔
4222
  prev = fsio_guard_chroot;
4223
  fsio_guard_chroot = guard;
149✔
4224

4225
  return prev;
4226
}
24✔
4227

24✔
4228
unsigned long pr_fsio_set_options(unsigned long opts) {
4229
  unsigned long prev;
24✔
4230

24✔
4231
  prev = fsio_opts;
4232
  fsio_opts = opts;
24✔
4233

4234
  return prev;
4235
}
3✔
4236

3✔
4237
int pr_fsio_set_use_mkdtemp(int value) {
4238
  int prev_value;
3✔
4239

4240
  if (value != TRUE &&
1✔
4241
      value != FALSE) {
1✔
4242
    errno = EINVAL;
4243
    return -1;
4244
  }
2✔
4245

4246
  prev_value = fsio_use_mkdtemp;
4247

2✔
4248
#ifdef HAVE_MKDTEMP
4249
  fsio_use_mkdtemp = value;
4250
#endif /* HAVE_MKDTEMP */
2✔
4251

4252
  return prev_value;
4253
}
4254

4255
/* Directory-specific "safe" chmod(2) which attempts to avoid/mitigate
4256
 * symlink attacks.
4257
 *
4258
 * To do this, we first open a file descriptor on the given path, using
4259
 * O_NOFOLLOW to avoid symlinks.  If the fd is not to a directory, it's
4260
 * an error.  Then we use fchmod(2) to set the perms.  There is still a
4261
 * race condition here, between the time the directory is created and
4262
 * when we call open(2).  But hopefully the ensuing checks on the fd
4263
 * (i.e. that it IS a directory) can mitigate that race.
4264
 *
4265
 * The fun part is ensuring that the OS/filesystem will give us an fd
4266
 * on a directory path (using O_RDONLY to avoid getting an EISDIR error),
4267
 * whilst being able to do a write (effectively) on the fd by changing
4268
 * its permissions.
1✔
4269
 */
1✔
4270
static int schmod_dir(pool *p, const char *path, mode_t perms, int use_root) {
1✔
4271
  int flags, fd, ignore_eacces = FALSE, ignore_eperm = FALSE, res, xerrno = 0;
1✔
4272
  struct stat st;
4273
  mode_t dir_mode;
4274

1✔
4275
  /* We're not using the pool at the moment. */
4276
  (void) p;
4277

4278
  /* Open an fd on the path using O_RDONLY|O_NOFOLLOW, so that we a)
4279
   * avoid symlinks, and b) get an fd on the (hopefully) directory.
1✔
4280
   */
4281
  flags = O_RDONLY;
1✔
4282
#ifdef O_NOFOLLOW
4283
  flags |= O_NOFOLLOW;
1✔
4284
#endif
1✔
4285
  fd = open(path, flags);
4286
  xerrno = errno;
1✔
4287

×
4288
  if (fd < 0) {
4289
    pr_trace_msg(trace_channel, 3,
×
4290
      "schmod: unable to open path '%s': %s", path, strerror(xerrno));
×
4291
    errno = xerrno;
4292
    return -1;
4293
  }
1✔
4294

1✔
4295
  res = fstat(fd, &st);
×
4296
  if (res < 0) {
4297
    xerrno = errno;
×
4298

4299
    (void) close(fd);
×
4300

4301
    pr_trace_msg(trace_channel, 3,
×
4302
      "schmod: unable to fstat path '%s': %s", path, strerror(xerrno));
×
4303
    errno = xerrno;
4304
    return -1;
4305
  }
4306

1✔
4307
  /* We expect only directories. */
×
4308
  if (!S_ISDIR(st.st_mode)) {
4309
    xerrno = ENOTDIR;
×
4310

4311
    (void) close(fd);
×
4312

4313
    pr_trace_msg(trace_channel, 3,
4314
      "schmod: unable to use path '%s': %s", path, strerror(xerrno));
4315

4316
    /* This is such an unexpected (and possibly malicious) situation that
4317
     * it warrants louder logging.
×
4318
     */
4319
    pr_log_pri(PR_LOG_WARNING,
4320
      "WARNING: detected non-directory '%s' during directory creation: "
4321
      "possible symlink attack", path);
×
4322

×
4323
    errno = xerrno;
4324
    return -1;
4325
  }
4326

4327
  /* Note that some filesystems (e.g. CIFS) may not actually create a
4328
   * directory with the expected 0700 mode.  If that is the case, then a
4329
   * subsequent chmod(2) on that directory will likely fail.  Thus we also
4330
   * double-check the mode of the directory created via mkdtemp(3), and
4331
   * attempt to mitigate Bug#4063.
1✔
4332
   */
1✔
4333
  dir_mode = (st.st_mode & ~S_IFMT);
×
4334
  if (dir_mode != 0700) {
4335
    ignore_eacces = ignore_eperm = TRUE;
×
4336

4337
    pr_trace_msg(trace_channel, 3,
4338
      "schmod: path '%s' has mode %04o, expected 0700", path, dir_mode);
4339

×
4340
    /* This is such an unexpected situation that it warrants some logging. */
4341
    pr_log_pri(PR_LOG_DEBUG,
4342
      "NOTICE: directory '%s' has unexpected mode %04o (expected 0700)", path,
4343
      dir_mode);
4344
  }
1✔
4345

×
4346
  if (use_root) {
4347
    PRIVS_ROOT
4348
  }
1✔
4349

1✔
4350
  res = fchmod(fd, perms);
4351
  xerrno = errno;
4352

4353
  /* Using fchmod(2) on a directory descriptor is not really kosher
4354
   * behavior, but appears to work on most filesystems.  Still, if we
4355
   * get an ENOENT back (as seen on some CIFS mounts, per Bug#4134), try
4356
   * using chmod(2) on the path.
1✔
4357
   */
1✔
4358
  if (res < 0 &&
×
4359
      xerrno == ENOENT) {
×
4360
    ignore_eacces = TRUE;
×
4361
    res = chmod(path, perms);
4362
    xerrno = errno;
4363
  }
1✔
4364

×
4365
  if (use_root) {
4366
    PRIVS_RELINQUISH
4367
  }
4368

1✔
4369
  /* At this point, succeed or fail, we're done with the fd. */
4370
  (void) close(fd);
1✔
4371

4372
  if (res < 0) {
4373
    /* Note: Some filesystem implementations, particularly via FUSE,
4374
     * may not actually implement ownership/permissions (e.g. FAT-based
4375
     * filesystems).  In such cases, chmod(2) et al will return ENOSYS
4376
     * (see Bug#3986).
4377
     *
4378
     * Other filesystem implementations (e.g. CIFS, depending on the mount
4379
     * options) will a chmod(2) that returns ENOENT (see Bug#4134).
4380
     *
4381
     * Should this fail the entire operation?  I'm of two minds about this.
4382
     * On the one hand, such filesystem behavior can undermine wider site
4383
     * security policies; on the other, prohibiting a MKD/MKDIR operation
4384
     * on such filesystems, deliberately used by the site admin, is not
4385
     * useful/friendly behavior.
4386
     *
4387
     * Maybe these exceptions for ENOSYS/ENOENT here should be made
4388
     * configurable?
4389
     */
×
4390

×
4391
    if (xerrno == ENOSYS ||
×
4392
        xerrno == ENOENT ||
×
4393
        (xerrno == EACCES && ignore_eacces == TRUE) ||
×
4394
        (xerrno == EPERM && ignore_eperm == TRUE)) {
4395
      pr_log_debug(DEBUG0, "schmod: unable to set perms %04o on "
4396
        "path '%s': %s (chmod(2) not supported by underlying filesystem?)",
×
4397
        perms, path, strerror(xerrno));
4398
      return 0;
4399
    }
×
4400

4401
    pr_trace_msg(trace_channel, 3,
4402
      "schmod: unable to set perms %04o on path '%s': %s", perms, path,
×
4403
      strerror(xerrno));
×
4404
    errno = xerrno;
4405
    return -1;
4406
  }
4407

4408
  return 0;
4409
}
4410

4411
/* "safe mkdir" variant of mkdir(2), uses mkdtemp(3), lchown(2), and
4412
 * rename(2) to create a directory which cannot be hijacked by a symlink
4413
 * race (hopefully) before the UserOwner/GroupOwner ownership changes are
4414
 * applied.
4✔
4415
 */
4416
int pr_fsio_smkdir(pool *p, const char *path, mode_t mode, uid_t uid,
4✔
4417
    gid_t gid) {
4✔
4418
  int res, set_sgid = FALSE, use_mkdtemp, use_root_chown = FALSE, xerrno = 0;
4✔
4419
  char *tmpl_path;
4✔
4420
  char *dst_dir, *tmpl;
4421
  size_t dst_dirlen, tmpl_len;
4✔
4422

4✔
4423
  if (p == NULL ||
2✔
4424
      path == NULL) {
2✔
4425
    errno = EINVAL;
4426
    return -1;
4427
  }
2✔
4428

4429
  pr_trace_msg(trace_channel, 9,
4430
    "smkdir: path '%s', mode %04o, UID %s, GID %s", path, (unsigned int) mode,
4431
    pr_uid2str(p, uid), pr_gid2str(p, gid));
2✔
4432

×
4433
  if (fsio_guard_chroot) {
×
4434
    res = chroot_allow_path(path);
4435
    if (res < 0) {
4436
      return -1;
4437
    }
4438
  }
2✔
4439

2✔
4440
  use_mkdtemp = fsio_use_mkdtemp;
4441
  if (use_mkdtemp == TRUE) {
4442

4443
    /* Note that using mkdtemp(3) is a way of dealing with Bug#3841.  The
4444
     * problem in question, though, only applies if root privs are used
4445
     * to set the ownership.  Thus if root privs are NOT needed, then there
4446
     * is no need to use mkdtemp(3).
4447
     */
1✔
4448

4449
    if (uid != (uid_t) -1) {
4450
      use_root_chown = TRUE;
×
4451

4452
    } else if (gid != (gid_t) -1) {
4453
      register unsigned int i;
×
4454

4455
      use_root_chown = TRUE;
4456

×
4457
      /* Check if session.fsgid is in session.gids.  If not, use root privs.  */
×
4458
      for (i = 0; i < session.gids->nelts; i++) {
4459
        gid_t *group_ids = session.gids->elts;
×
4460

4461
        if (group_ids[i] == gid) {
4462
          use_root_chown = FALSE;
4463
          break;
4464
        }
4465
      }
4466
    }
×
4467

4468
    if (use_root_chown == FALSE) {
4469
      use_mkdtemp = FALSE;
4470
    }
4471
  }
4472

2✔
4473
#ifdef HAVE_MKDTEMP
1✔
4474
  if (use_mkdtemp == TRUE) {
1✔
4475
    char *ptr;
4476
    struct stat st;
1✔
4477

1✔
4478
    ptr = strrchr(path, '/');
×
4479
    if (ptr == NULL) {
×
4480
      errno = EINVAL;
4481
      return -1;
4482
    }
1✔
4483

1✔
4484
    if (ptr != path) {
1✔
4485
      dst_dirlen = (ptr - path);
4486
      dst_dir = pstrndup(p, path, dst_dirlen);
4487

4488
    } else {
4489
      dst_dirlen = 1;
4490
      dst_dir = "/";
4491
    }
1✔
4492

1✔
4493
    res = lstat(dst_dir, &st);
×
4494
    if (res < 0) {
4495
      xerrno = errno;
×
4496

4497
      pr_log_pri(PR_LOG_WARNING,
4498
        "smkdir: unable to lstat(2) parent directory '%s': %s", dst_dir,
×
4499
        strerror(xerrno));
4500
      pr_trace_msg(trace_channel, 1,
4501
        "smkdir: unable to lstat(2) parent directory '%s': %s", dst_dir,
4502
        strerror(xerrno));
×
4503

×
4504
      errno = xerrno;
4505
      return -1;
4506
    }
1✔
4507

4508
    if (!S_ISDIR(st.st_mode) &&
×
4509
        !S_ISLNK(st.st_mode)) {
×
4510
      errno = EPERM;
4511
      return -1;
4512
    }
1✔
4513

×
4514
    if (st.st_mode & S_ISGID) {
4515
      set_sgid = TRUE;
4516
    }
4517

4518
    /* Allocate enough space for the temporary name: the length of the
4519
     * destination directory, a slash, 9 X's, 3 for the prefix, and 1 for the
4520
     * trailing NUL.
1✔
4521
     */
1✔
4522
    tmpl_len = dst_dirlen + 15;
1✔
4523
    tmpl = pcalloc(p, tmpl_len);
4524
    pr_snprintf(tmpl, tmpl_len-1, "%s/.dstXXXXXXXXX",
4525
      dst_dirlen > 1 ? dst_dir : "");
4526

4527
    /* Use mkdtemp(3) to create the temporary directory (in the same destination
4528
     * directory as the target path).
1✔
4529
     */
1✔
4530
    tmpl_path = mkdtemp(tmpl);
×
4531
    if (tmpl_path == NULL) {
4532
      xerrno = errno;
×
4533

4534
      pr_log_pri(PR_LOG_WARNING,
4535
        "smkdir: mkdtemp(3) failed to create directory using '%s': %s", tmpl,
×
4536
        strerror(xerrno));
4537
      pr_trace_msg(trace_channel, 1,
4538
        "smkdir: mkdtemp(3) failed to create directory using '%s': %s", tmpl,
4539
        strerror(xerrno));
×
4540

×
4541
      errno = xerrno;
4542
      return -1;
4543
    }
4544

1✔
4545
  } else {
1✔
4546
    res = pr_fsio_mkdir(path, mode);
×
4547
    if (res < 0) {
4548
      xerrno = errno;
×
4549

4550
      pr_trace_msg(trace_channel, 1,
4551
        "mkdir(2) failed to create directory '%s' with perms %04o: %s", path,
4552
        mode, strerror(xerrno));
×
4553

×
4554
      errno = xerrno;
4555
      return -1;
4556
    }
1✔
4557

4558
    tmpl_path = pstrdup(p, path);
4559
  }
4560
#else
4561

4562
  res = pr_fsio_mkdir(path, mode);
4563
  if (res < 0) {
4564
    xerrno = errno;
4565

4566
    pr_trace_msg(trace_channel, 1,
4567
      "mkdir(2) failed to create directory '%s' with perms %04o: %s", path,
4568
      mode, strerror(xerrno));
4569

4570
    errno = xerrno;
4571
    return -1;
4572
  }
4573

4574
  tmpl_path = pstrdup(p, path);
4575
#endif /* HAVE_MKDTEMP */
2✔
4576

1✔
4577
  if (use_mkdtemp == TRUE) {
4578
    mode_t mask, *dir_umask, perms;
4579

4580
    /* mkdtemp(3) creates a directory with 0700 perms; we are given the
4581
     * target mode (modulo the configured Umask).
1✔
4582
     */
1✔
4583
    dir_umask = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
4584
    if (dir_umask == NULL) {
4585
      /* If Umask was configured with a single parameter, then DirUmask
4586
       * would not be present; we still should check for Umask.
1✔
4587
       */
4588
      dir_umask = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
4589
    }
1✔
4590

×
4591
    if (dir_umask) {
4592
      mask = *dir_umask;
4593

4594
    } else {
4595
      mask = (mode_t) 0022;
4596
    }
1✔
4597

4598
    perms = (mode & ~mask);
1✔
4599

×
4600
    if (set_sgid) {
4601
      perms |= S_ISGID;
4602
    }
4603

4604
    /* If we're setting the SGID bit, we need to use root privs, in order
4605
     * to reliably set the SGID bit.  Sigh.
1✔
4606
     */
1✔
4607
    res = schmod_dir(p, tmpl_path, perms, set_sgid);
4608
    xerrno = errno;
1✔
4609

×
4610
    if (set_sgid) {
×
4611
      if (res < 0 &&
4612
          xerrno == EPERM) {
4613
        /* Try again, this time without root privs.  NFS situations which
4614
         * squash root privs could cause the above chmod(2) to fail; it
4615
         * might succeed now that we've dropped root privs (Bug#3962).
×
4616
         */
×
4617
        res = schmod_dir(p, tmpl_path, perms, FALSE);
4618
        xerrno = errno;
4619
      }
4620
    }
1✔
4621

×
4622
    if (res < 0) {
4623
      pr_log_pri(PR_LOG_WARNING, "chmod(%s) failed: %s", tmpl_path,
4624
        strerror(xerrno));
×
4625

4626
      (void) rmdir(tmpl_path);
×
4627

×
4628
      errno = xerrno;
4629
      return -1;
4630
    }
4631
  }
2✔
4632

2✔
4633
  if (uid != (uid_t) -1) {
1✔
4634
    if (use_root_chown) {
4635
      PRIVS_ROOT
4636
    }
2✔
4637

2✔
4638
    res = pr_fsio_lchown(tmpl_path, uid, gid);
4639
    xerrno = errno;
2✔
4640

1✔
4641
    if (use_root_chown) {
4642
      PRIVS_RELINQUISH
4643
    }
2✔
4644

×
4645
    if (res < 0) {
4646
      pr_log_pri(PR_LOG_WARNING, "lchown(%s) as root failed: %s", tmpl_path,
4647
        strerror(xerrno));
4648

2✔
4649
    } else {
2✔
4650
      if (gid != (gid_t) -1) {
4651
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s, GID %s successful",
4652
          tmpl_path, pr_uid2str(p, uid), pr_gid2str(p, gid));
4653

×
4654
      } else {
4655
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s successful",
4656
          tmpl_path, pr_uid2str(NULL, uid));
4657
      }
4658
    }
×
4659

×
4660
  } else if (gid != (gid_t) -1) {
×
4661
    if (use_root_chown) {
4662
      PRIVS_ROOT
4663
    }
×
4664

×
4665
    res = pr_fsio_lchown(tmpl_path, (uid_t) -1, gid);
4666
    xerrno = errno;
×
4667

×
4668
    if (use_root_chown) {
4669
      PRIVS_RELINQUISH
4670
    }
×
4671

×
4672
    if (res < 0) {
4673
      pr_log_pri(PR_LOG_WARNING, "%slchown(%s) failed: %s",
4674
        use_root_chown ? "root " : "", tmpl_path, strerror(xerrno));
4675

×
4676
    } else {
4677
      pr_log_debug(DEBUG2, "%slchown(%s) to GID %s successful",
4678
        use_root_chown ? "root " : "", tmpl_path, pr_gid2str(p, gid));
4679
    }
4680
  }
2✔
4681

4682
  if (use_mkdtemp == TRUE) {
4683
    /* Use rename(2) to move the temporary directory into place at the
4684
     * target path.
1✔
4685
     */
1✔
4686
    res = rename(tmpl_path, path);
×
4687
    if (res < 0) {
4688
      xerrno = errno;
×
4689

4690
      pr_log_pri(PR_LOG_INFO, "renaming '%s' to '%s' failed: %s", tmpl_path,
4691
        path, strerror(xerrno));
×
4692

4693
      (void) rmdir(tmpl_path);
4694

×
4695
#ifdef ENOTEMPTY
4696
      if (xerrno == ENOTEMPTY) {
4697
        /* If the rename(2) failed with "Directory not empty" (ENOTEMPTY),
4698
         * then change the errno to "File exists" (EEXIST), so that the
4699
         * error reported to the client is more indicative of the actual
4700
         * cause.
×
4701
         */
4702
        xerrno = EEXIST;
4703
      }
4704
#endif /* ENOTEMPTY */
×
4705

×
4706
      errno = xerrno;
4707
      return -1;
4708
    }
4709
  }
2✔
4710

2✔
4711
  pr_fs_clear_cache2(path);
4712
  return 0;
4713
}
11✔
4714

11✔
4715
int pr_fsio_rmdir(const char *path) {
11✔
4716
  int res;
4717
  pr_fs_t *fs;
11✔
4718

1✔
4719
  if (path == NULL) {
1✔
4720
    errno = EINVAL;
4721
    return -1;
4722
  }
10✔
4723

10✔
4724
  fs = lookup_dir_fs(path, FSIO_DIR_RMDIR);
4725
  if (fs == NULL) {
4726
    return -1;
4727
  }
4728

4729
  /* Find the first non-NULL custom rmdir handler.  If there are none,
4730
   * use the system rmdir.
10✔
4731
   */
4732
  while (fs && fs->fs_next && !fs->rmdir) {
4733
    fs = fs->fs_next;
4734
  }
10✔
4735

4736
  pr_trace_msg(trace_channel, 8, "using %s rmdir() for path '%s'", fs->fs_name,
10✔
4737
    path);
10✔
4738
  res = (fs->rmdir)(fs, path);
4✔
4739
  if (res == 0) {
4740
    pr_fs_clear_cache2(path);
4741
  }
4742

4743
  return res;
4744
}
2✔
4745

2✔
4746
int pr_fsio_rmdir_with_error(pool *p, const char *path, pr_error_t **err) {
4747
  int res;
2✔
4748

2✔
4749
  res = pr_fsio_rmdir(path);
2✔
4750
  if (res < 0) {
4751
    int xerrno = errno;
2✔
4752

2✔
4753
    if (p != NULL &&
1✔
4754
        err != NULL) {
1✔
4755
      *err = pr_error_create(p, xerrno);
×
4756
      if (pr_error_explain_rmdir(*err, path) < 0) {
×
4757
        pr_error_destroy(*err);
4758
        *err = NULL;
4759
      }
4760
    }
2✔
4761

4762
    errno = xerrno;
4763
  }
2✔
4764

4765
  return res;
4766
}
46✔
4767

46✔
4768
int pr_fsio_stat(const char *path, struct stat *st) {
4769
  pr_fs_t *fs = NULL;
46✔
4770

46✔
4771
  if (path == NULL ||
2✔
4772
      st == NULL) {
2✔
4773
    errno = EINVAL;
4774
    return -1;
4775
  }
44✔
4776

44✔
4777
  fs = lookup_file_fs(path, NULL, FSIO_FILE_STAT);
4778
  if (fs == NULL) {
4779
    return -1;
4780
  }
4781

4782
  /* Find the first non-NULL custom stat handler.  If there are none,
4783
   * use the system stat.
44✔
4784
   */
4785
  while (fs && fs->fs_next && !fs->stat) {
4786
    fs = fs->fs_next;
4787
  }
44✔
4788

4789
  pr_trace_msg(trace_channel, 8, "using %s stat() for path '%s'", fs->fs_name,
44✔
4790
    path);
4791
  return fs_cache_stat(fs ? fs : root_fs, path, st);
4792
}
3✔
4793

4794
int pr_fsio_stat_with_error(pool *p, const char *path, struct stat *st,
3✔
4795
    pr_error_t **err) {
4796
  int res;
3✔
4797

3✔
4798
  res = pr_fsio_stat(path, st);
3✔
4799
  if (res < 0) {
4800
    int xerrno = errno;
3✔
4801

3✔
4802
    if (p != NULL &&
2✔
4803
        err != NULL) {
2✔
4804
      *err = pr_error_create(p, xerrno);
1✔
4805
      if (pr_error_explain_stat(*err, path, st) < 0) {
1✔
4806
        pr_error_destroy(*err);
4807
        *err = NULL;
4808
      }
4809
    }
3✔
4810

4811
    errno = xerrno;
4812
  }
3✔
4813

4814
  return res;
4815
}
37✔
4816

37✔
4817
int pr_fsio_fstat(pr_fh_t *fh, struct stat *st) {
37✔
4818
  int res;
4819
  pr_fs_t *fs;
37✔
4820

37✔
4821
  if (fh == NULL ||
1✔
4822
      st == NULL) {
1✔
4823
    errno = EINVAL;
4824
    return -1;
4825
  }
4826

4827
  /* Find the first non-NULL custom fstat handler.  If there are none,
4828
   * use the system fstat.
36✔
4829
   */
36✔
4830
  fs = fh->fh_fs;
4831
  while (fs && fs->fs_next && !fs->fstat) {
4832
    fs = fs->fs_next;
4833
  }
36✔
4834

4835
  pr_trace_msg(trace_channel, 8, "using %s fstat() for path '%s'", fs->fs_name,
36✔
4836
    fh->fh_path);
4837
  res = (fs->fstat)(fh, fh->fh_fd, st);
36✔
4838

4839
  return res;
4840
}
34✔
4841

34✔
4842
int pr_fsio_lstat(const char *path, struct stat *st) {
4843
  pr_fs_t *fs;
34✔
4844

34✔
4845
  if (path == NULL ||
2✔
4846
      st == NULL) {
2✔
4847
    errno = EINVAL;
4848
    return -1;
4849
  }
32✔
4850

32✔
4851
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LSTAT);
4852
  if (fs == NULL) {
4853
    return -1;
4854
  }
4855

4856
  /* Find the first non-NULL custom lstat handler.  If there are none,
4857
   * use the system lstat.
32✔
4858
   */
4859
  while (fs && fs->fs_next && !fs->lstat) {
4860
    fs = fs->fs_next;
4861
  }
32✔
4862

4863
  pr_trace_msg(trace_channel, 8, "using %s lstat() for path '%s'", fs->fs_name,
32✔
4864
    path);
4865
  return fs_cache_lstat(fs ? fs : root_fs, path, st);
4866
}
3✔
4867

4868
int pr_fsio_lstat_with_error(pool *p, const char *path, struct stat *st,
3✔
4869
    pr_error_t **err) {
4870
  int res;
3✔
4871

3✔
4872
  res = pr_fsio_lstat(path, st);
3✔
4873
  if (res < 0) {
4874
    int xerrno = errno;
3✔
4875

3✔
4876
    if (p != NULL &&
2✔
4877
        err != NULL) {
2✔
4878
      *err = pr_error_create(p, xerrno);
1✔
4879
      if (pr_error_explain_lstat(*err, path, st) < 0) {
1✔
4880
        pr_error_destroy(*err);
4881
        *err = NULL;
4882
      }
4883
    }
3✔
4884

4885
    errno = xerrno;
4886
  }
3✔
4887

4888
  return res;
4889
}
29✔
4890

29✔
4891
int pr_fsio_readlink(const char *path, char *buf, size_t buflen) {
29✔
4892
  int res;
4893
  pr_fs_t *fs;
29✔
4894

29✔
4895
  if (path == NULL ||
1✔
4896
      buf == NULL) {
1✔
4897
    errno = EINVAL;
4898
    return -1;
4899
  }
28✔
4900

28✔
4901
  fs = lookup_file_fs(path, NULL, FSIO_FILE_READLINK);
4902
  if (fs == NULL) {
4903
    return -1;
4904
  }
4905

4906
  /* Find the first non-NULL custom readlink handler.  If there are none,
4907
   * use the system readlink.
28✔
4908
   */
4909
  while (fs && fs->fs_next && !fs->readlink) {
4910
    fs = fs->fs_next;
4911
  }
28✔
4912

4913
  pr_trace_msg(trace_channel, 8, "using %s readlink() for path '%s'",
28✔
4914
    fs->fs_name, path);
4915
  res = (fs->readlink)(fs, path, buf, buflen);
28✔
4916

4917
  return res;
4918
}
4919

4920
/* pr_fs_glob() is just a wrapper for glob(3), setting the various gl_
4921
 * callbacks to our fs functions.
3✔
4922
 */
4923
int pr_fs_glob(const char *pattern, int flags,
4924
    int (*errfunc)(const char *, int), glob_t *pglob) {
3✔
4925

3✔
4926
  if (pattern == NULL ||
2✔
4927
      pglob == NULL) {
2✔
4928
    errno = EINVAL;
4929
    return -1;
4930
  }
1✔
4931

4932
  flags |= GLOB_ALTDIRFUNC;
1✔
4933

1✔
4934
  pglob->gl_closedir = (void (*)(void *)) pr_fsio_closedir;
1✔
4935
  pglob->gl_readdir = pr_fsio_readdir;
1✔
4936
  pglob->gl_opendir = pr_fsio_opendir;
1✔
4937
  pglob->gl_lstat = pr_fsio_lstat;
4938
  pglob->gl_stat = pr_fsio_stat;
1✔
4939

4940
  return glob(pattern, flags, errfunc, pglob);
4941
}
2✔
4942

2✔
4943
void pr_fs_globfree(glob_t *pglob) {
1✔
4944
  if (pglob != NULL) {
4945
    globfree(pglob);
2✔
4946
  }
4947
}
10✔
4948

10✔
4949
int pr_fsio_rename(const char *rnfr, const char *rnto) {
10✔
4950
  int res;
4951
  pr_fs_t *from_fs, *to_fs, *fs;
10✔
4952

10✔
4953
  if (rnfr == NULL ||
2✔
4954
      rnto == NULL) {
2✔
4955
    errno = EINVAL;
4956
    return -1;
4957
  }
8✔
4958

8✔
4959
  from_fs = lookup_file_fs(rnfr, NULL, FSIO_FILE_RENAME);
4960
  if (from_fs == NULL) {
4961
    return -1;
4962
  }
8✔
4963

8✔
4964
  to_fs = lookup_file_fs(rnto, NULL, FSIO_FILE_RENAME);
4965
  if (to_fs == NULL) {
4966
    return -1;
4967
  }
8✔
4968

8✔
4969
  if (from_fs->allow_xdev_rename == FALSE ||
×
4970
      to_fs->allow_xdev_rename == FALSE) {
×
4971
    if (from_fs != to_fs) {
×
4972
      errno = EXDEV;
4973
      return -1;
4974
    }
4975
  }
4976

4977
  fs = to_fs;
4978

4979
  /* Find the first non-NULL custom rename handler.  If there are none,
4980
   * use the system rename.
8✔
4981
   */
4982
  while (fs && fs->fs_next && !fs->rename) {
4983
    fs = fs->fs_next;
4984
  }
8✔
4985

4986
  pr_trace_msg(trace_channel, 8, "using %s rename() for paths '%s', '%s'",
8✔
4987
    fs->fs_name, rnfr, rnto);
8✔
4988
  res = (fs->rename)(fs, rnfr, rnto);
1✔
4989
  if (res == 0) {
1✔
4990
    pr_fs_clear_cache2(rnfr);
4991
    pr_fs_clear_cache2(rnto);
4992
  }
4993

4994
  return res;
4995
}
3✔
4996

4997
int pr_fsio_rename_with_error(pool *p, const char *rnfr, const char *rnto,
3✔
4998
    pr_error_t **err) {
4999
  int res;
3✔
5000

3✔
5001
  res = pr_fsio_rename(rnfr, rnto);
3✔
5002
  if (res < 0) {
5003
    int xerrno = errno;
3✔
5004

3✔
5005
    if (p != NULL &&
2✔
5006
        err != NULL) {
2✔
5007
      *err = pr_error_create(p, xerrno);
1✔
5008
      if (pr_error_explain_rename(*err, rnfr, rnto) < 0) {
1✔
5009
        pr_error_destroy(*err);
5010
        *err = NULL;
5011
      }
5012
    }
3✔
5013

5014
    errno = xerrno;
5015
  }
3✔
5016

5017
  return res;
5018
}
76✔
5019

76✔
5020
int pr_fsio_unlink(const char *name) {
76✔
5021
  int res;
5022
  pr_fs_t *fs;
76✔
5023

1✔
5024
  if (name == NULL) {
1✔
5025
    errno = EINVAL;
5026
    return -1;
5027
  }
75✔
5028

75✔
5029
  fs = lookup_file_fs(name, NULL, FSIO_FILE_UNLINK);
5030
  if (fs == NULL) {
5031
    return -1;
5032
  }
5033

5034
  /* Find the first non-NULL custom unlink handler.  If there are none,
5035
   * use the system unlink.
75✔
5036
   */
5037
  while (fs && fs->fs_next && !fs->unlink) {
5038
    fs = fs->fs_next;
5039
  }
75✔
5040

5041
  pr_trace_msg(trace_channel, 8, "using %s unlink() for path '%s'",
75✔
5042
    fs->fs_name, name);
75✔
5043
  res = (fs->unlink)(fs, name);
28✔
5044
  if (res == 0) {
5045
    pr_fs_clear_cache2(name);
5046
  }
5047

5048
  return res;
5049
}
3✔
5050

3✔
5051
int pr_fsio_unlink_with_error(pool *p, const char *path, pr_error_t **err) {
5052
  int res;
3✔
5053

3✔
5054
  res = pr_fsio_unlink(path);
3✔
5055
  if (res < 0) {
5056
    int xerrno = errno;
3✔
5057

3✔
5058
    if (p != NULL &&
2✔
5059
        err != NULL) {
2✔
5060
      *err = pr_error_create(p, xerrno);
1✔
5061
      if (pr_error_explain_unlink(*err, path) < 0) {
1✔
5062
        pr_error_destroy(*err);
5063
        *err = NULL;
5064
      }
5065
    }
3✔
5066

5067
    errno = xerrno;
5068
  }
3✔
5069

5070
  return res;
5071
}
12✔
5072

12✔
5073
pr_fh_t *pr_fsio_open_canon(const char *name, int flags) {
12✔
5074
  char *deref = NULL;
12✔
5075
  pool *tmp_pool = NULL;
12✔
5076
  pr_fh_t *fh = NULL;
5077
  pr_fs_t *fs = NULL;
12✔
5078

1✔
5079
  if (name == NULL) {
1✔
5080
    errno = EINVAL;
5081
    return NULL;
5082
  }
11✔
5083

11✔
5084
  fs = lookup_file_canon_fs(name, &deref, FSIO_FILE_OPEN);
5085
  if (fs == NULL) {
5086
    return NULL;
5087
  }
5088

11✔
5089
  /* Allocate a filehandle. */
11✔
5090
  tmp_pool = make_sub_pool(fs->fs_pool);
5091
  pr_pool_tag(tmp_pool, "pr_fsio_open_canon() subpool");
11✔
5092

11✔
5093
  fh = pcalloc(tmp_pool, sizeof(pr_fh_t));
11✔
5094
  fh->fh_pool = tmp_pool;
11✔
5095
  fh->fh_path = pstrdup(fh->fh_pool, name);
11✔
5096
  fh->fh_fd = -1;
11✔
5097
  fh->fh_buf = NULL;
5098
  fh->fh_fs = fs;
5099

5100
  /* Find the first non-NULL custom open handler.  If there are none,
5101
   * use the system open.
11✔
5102
   */
5103
  while (fs && fs->fs_next && !fs->open) {
5104
    fs = fs->fs_next;
5105
  }
11✔
5106

5107
  pr_trace_msg(trace_channel, 8, "using %s open() for path '%s'", fs->fs_name,
11✔
5108
    name);
11✔
5109
  fh->fh_fd = (fs->open)(fh, deref, flags);
2✔
5110
  if (fh->fh_fd < 0) {
5111
    int xerrno = errno;
2✔
5112

5113
    destroy_pool(fh->fh_pool);
2✔
5114

2✔
5115
    errno = xerrno;
5116
    return NULL;
5117
  }
9✔
5118

5119
  if ((flags & O_CREAT) ||
×
5120
      (flags & O_TRUNC)) {
5121
    pr_fs_clear_cache2(name);
5122
  }
9✔
5123

×
5124
  if (fcntl(fh->fh_fd, F_SETFD, FD_CLOEXEC) < 0) {
×
5125
    if (errno != EBADF) {
5126
      pr_trace_msg(trace_channel, 1, "error setting CLOEXEC on file fd %d: %s",
5127
        fh->fh_fd, strerror(errno));
5128
    }
5129
  }
5130

5131
  return fh;
5132
}
72✔
5133

72✔
5134
pr_fh_t *pr_fsio_open(const char *name, int flags) {
72✔
5135
  pool *tmp_pool = NULL;
72✔
5136
  pr_fh_t *fh = NULL;
5137
  pr_fs_t *fs = NULL;
72✔
5138

1✔
5139
  if (name == NULL) {
1✔
5140
    errno = EINVAL;
5141
    return NULL;
5142
  }
71✔
5143

71✔
5144
  fs = lookup_file_fs(name, NULL, FSIO_FILE_OPEN);
5145
  if (fs == NULL) {
5146
    return NULL;
5147
  }
5148

71✔
5149
  /* Allocate a filehandle. */
71✔
5150
  tmp_pool = make_sub_pool(fs->fs_pool);
5151
  pr_pool_tag(tmp_pool, "pr_fsio_open() subpool");
71✔
5152

71✔
5153
  fh = pcalloc(tmp_pool, sizeof(pr_fh_t));
71✔
5154
  fh->fh_pool = tmp_pool;
71✔
5155
  fh->fh_path = pstrdup(fh->fh_pool, name);
71✔
5156
  fh->fh_fd = -1;
71✔
5157
  fh->fh_buf = NULL;
5158
  fh->fh_fs = fs;
5159

5160
  /* Find the first non-NULL custom open handler.  If there are none,
5161
   * use the system open.
71✔
5162
   */
5163
  while (fs && fs->fs_next && !fs->open) {
5164
    fs = fs->fs_next;
5165
  }
71✔
5166

5167
  pr_trace_msg(trace_channel, 8, "using %s open() for path '%s'", fs->fs_name,
71✔
5168
    name);
71✔
5169
  fh->fh_fd = (fs->open)(fh, name, flags);
11✔
5170
  if (fh->fh_fd < 0) {
5171
    int xerrno = errno;
11✔
5172

5173
    destroy_pool(fh->fh_pool);
11✔
5174

11✔
5175
    errno = xerrno;
5176
    return NULL;
5177
  }
60✔
5178

5179
  if ((flags & O_CREAT) ||
39✔
5180
      (flags & O_TRUNC)) {
5181
    pr_fs_clear_cache2(name);
5182
  }
60✔
5183

×
5184
  if (fcntl(fh->fh_fd, F_SETFD, FD_CLOEXEC) < 0) {
×
5185
    if (errno != EBADF) {
5186
      pr_trace_msg(trace_channel, 1, "error setting CLOEXEC on file fd %d: %s",
5187
        fh->fh_fd, strerror(errno));
5188
    }
5189
  }
5190

5191
  return fh;
5192
}
3✔
5193

5194
pr_fh_t *pr_fsio_open_with_error(pool *p, const char *name, int flags,
3✔
5195
    pr_error_t **err) {
5196
  pr_fh_t *fh;
3✔
5197

3✔
5198
  fh = pr_fsio_open(name, flags);
3✔
5199
  if (fh == NULL) {
5200
    int xerrno = errno;
3✔
5201

3✔
5202
    if (p != NULL &&
2✔
5203
        err != NULL) {
2✔
5204
      *err = pr_error_create(p, xerrno);
1✔
5205
      if (pr_error_explain_open(*err, name, flags, PR_OPEN_MODE) < 0) {
1✔
5206
        pr_error_destroy(*err);
5207
        *err = NULL;
5208
      }
5209
    }
3✔
5210

5211
    errno = xerrno;
5212
  }
3✔
5213

5214
  return fh;
5215
}
71✔
5216

71✔
5217
int pr_fsio_close(pr_fh_t *fh) {
71✔
5218
  int res = 0, xerrno = 0;
5219
  pr_fs_t *fs;
71✔
5220

4✔
5221
  if (fh == NULL) {
4✔
5222
    errno = EINVAL;
5223
    return -1;
5224
  }
5225

5226
  /* Find the first non-NULL custom close handler.  If there are none,
5227
   * use the system close.
67✔
5228
   */
67✔
5229
  fs = fh->fh_fs;
5230
  while (fs && fs->fs_next && !fs->close) {
5231
    fs = fs->fs_next;
5232
  }
67✔
5233

5234
  pr_trace_msg(trace_channel, 8, "using %s close() for path '%s'", fs->fs_name,
67✔
5235
    fh->fh_path);
67✔
5236
  res = (fs->close)(fh, fh->fh_fd);
5237
  xerrno = errno;
67✔
5238

67✔
5239
  if (res == 0) {
5240
    pr_fs_clear_cache2(fh->fh_path);
5241
  }
5242

67✔
5243
  /* Make sure to scrub any buffered memory, too. */
15✔
5244
  if (fh->fh_buf != NULL) {
5245
    pr_buffer_t *pbuf;
15✔
5246

15✔
5247
    pbuf = fh->fh_buf;
5248
    pr_memscrub(pbuf->buf, pbuf->buflen);
5249
  }
67✔
5250

67✔
5251
  if (fh->fh_pool != NULL) {
5252
    destroy_pool(fh->fh_pool);
5253
  }
67✔
5254

67✔
5255
  errno = xerrno;
5256
  return res;
5257
}
3✔
5258

3✔
5259
int pr_fsio_close_with_error(pool *p, pr_fh_t *fh, pr_error_t **err) {
5260
  int res;
3✔
5261

3✔
5262
  res = pr_fsio_close(fh);
3✔
5263
  if (res < 0) {
5264
    int xerrno = errno;
3✔
5265

3✔
5266
    if (p != NULL &&
2✔
5267
        err != NULL) {
5268
      int fd = -1;
2✔
5269

5270
      *err = pr_error_create(p, xerrno);
2✔
5271

×
5272
      if (fh != NULL) {
5273
        fd = fh->fh_fd;
5274
      }
2✔
5275

1✔
5276
      if (pr_error_explain_close(*err, fd) < 0) {
1✔
5277
        pr_error_destroy(*err);
5278
        *err = NULL;
5279
      }
5280
    }
3✔
5281

5282
    errno = xerrno;
5283
  }
3✔
5284

5285
  return res;
5286
}
4✔
5287

4✔
5288
ssize_t pr_fsio_pread(pr_fh_t *fh, void *buf, size_t size, off_t offset) {
4✔
5289
  ssize_t res;
5290
  pr_fs_t *fs;
4✔
5291

4✔
5292
  if (fh == NULL ||
5293
      buf == NULL ||
3✔
5294
      size == 0) {
3✔
5295
    errno = EINVAL;
5296
    return -1;
5297
  }
5298

5299
  /* Find the first non-NULL custom pread handler.  If there are none,
5300
   * use the system pread.
1✔
5301
   */
1✔
5302
  fs = fh->fh_fs;
5303
  while (fs && fs->fs_next && !fs->pread) {
5304
    fs = fs->fs_next;
5305
  }
1✔
5306

5307
  pr_trace_msg(trace_channel, 8, "using %s pread() for path '%s' (%lu bytes, %"
5308
    PR_LU " offset)", fs->fs_name, fh->fh_path, (unsigned long) size,
1✔
5309
    (pr_off_t) offset);
5310
  res = (fs->pread)(fh, fh->fh_fd, buf, size, offset);
1✔
5311

5312
  return res;
5313
}
41✔
5314

41✔
5315
int pr_fsio_read(pr_fh_t *fh, char *buf, size_t size) {
41✔
5316
  int res;
5317
  pr_fs_t *fs;
41✔
5318

41✔
5319
  if (fh == NULL ||
5320
      buf == NULL ||
6✔
5321
      size == 0) {
6✔
5322
    errno = EINVAL;
5323
    return -1;
5324
  }
5325

5326
  /* Find the first non-NULL custom read handler.  If there are none,
5327
   * use the system read.
35✔
5328
   */
35✔
5329
  fs = fh->fh_fs;
5330
  while (fs && fs->fs_next && !fs->read) {
5331
    fs = fs->fs_next;
5332
  }
35✔
5333

5334
  pr_trace_msg(trace_channel, 8, "using %s read() for path '%s' (%lu bytes)",
35✔
5335
    fs->fs_name, fh->fh_path, (unsigned long) size);
5336
  res = (fs->read)(fh, fh->fh_fd, buf, size);
35✔
5337

5338
  return res;
5339
}
3✔
5340

5341
int pr_fsio_read_with_error(pool *p, pr_fh_t *fh, char *buf, size_t sz,
3✔
5342
    pr_error_t **err) {
5343
  int res;
3✔
5344

3✔
5345
  res = pr_fsio_read(fh, buf, sz);
3✔
5346
  if (res < 0) {
5347
    int xerrno = errno;
3✔
5348

3✔
5349
    if (p != NULL &&
2✔
5350
        err != NULL) {
5351
      int fd = -1;
2✔
5352

×
5353
      if (fh != NULL) {
5354
        fd = fh->fh_fd;
5355
      }
2✔
5356

2✔
5357
      *err = pr_error_create(p, xerrno);
1✔
5358
      if (pr_error_explain_read(*err, fd, buf, sz) < 0) {
1✔
5359
        pr_error_destroy(*err);
5360
        *err = NULL;
5361
      }
5362
    }
3✔
5363

5364
    errno = xerrno;
5365
  }
3✔
5366

5367
  return res;
5368
}
4✔
5369

5370
ssize_t pr_fsio_pwrite(pr_fh_t *fh, const void *buf, size_t size,
4✔
5371
    off_t offset) {
4✔
5372
  ssize_t res;
5373
  pr_fs_t *fs;
4✔
5374

4✔
5375
  if (fh == NULL ||
2✔
5376
      buf == NULL) {
2✔
5377
    errno = EINVAL;
5378
    return -1;
5379
  }
5380

5381
  /* Find the first non-NULL custom pwrite handler.  If there are none,
5382
   * use the system pwrite.
2✔
5383
   */
2✔
5384
  fs = fh->fh_fs;
5385
  while (fs && fs->fs_next && !fs->pwrite) {
5386
    fs = fs->fs_next;
5387
  }
2✔
5388

5389
  pr_trace_msg(trace_channel, 8, "using %s pwrite() for path '%s' (%lu bytes, %"
5390
    PR_LU " offset)", fs->fs_name, fh->fh_path, (unsigned long) size,
2✔
5391
    (pr_off_t) offset);
5392
  res = (fs->pwrite)(fh, fh->fh_fd, buf, size, offset);
2✔
5393

5394
  return res;
5395
}
23✔
5396

23✔
5397
int pr_fsio_write(pr_fh_t *fh, const char *buf, size_t size) {
23✔
5398
  int res;
5399
  pr_fs_t *fs;
23✔
5400

23✔
5401
  if (fh == NULL ||
5✔
5402
      buf == NULL) {
5✔
5403
    errno = EINVAL;
5404
    return -1;
5405
  }
5406

5407
  /* Find the first non-NULL custom write handler.  If there are none,
5408
   * use the system write.
18✔
5409
   */
18✔
5410
  fs = fh->fh_fs;
5411
  while (fs && fs->fs_next && !fs->write) {
5412
    fs = fs->fs_next;
5413
  }
18✔
5414

5415
  pr_trace_msg(trace_channel, 8, "using %s write() for path '%s' (%lu bytes)",
18✔
5416
    fs->fs_name, fh->fh_path, (unsigned long) size);
5417
  res = (fs->write)(fh, fh->fh_fd, buf, size);
18✔
5418

5419
  return res;
5420
}
3✔
5421

5422
int pr_fsio_write_with_error(pool *p, pr_fh_t *fh, const char *buf, size_t sz,
3✔
5423
    pr_error_t **err) {
5424
  int res;
3✔
5425

3✔
5426
  res = pr_fsio_write(fh, buf, sz);
3✔
5427
  if (res < 0) {
5428
    int xerrno = errno;
3✔
5429

3✔
5430
    if (p != NULL &&
2✔
5431
        err != NULL) {
5432
      int fd = -1;
2✔
5433

×
5434
      if (fh != NULL) {
5435
        fd = fh->fh_fd;
5436
      }
2✔
5437

2✔
5438
      *err = pr_error_create(p, xerrno);
1✔
5439
      if (pr_error_explain_write(*err, fd, buf, sz) < 0) {
1✔
5440
        pr_error_destroy(*err);
5441
        *err = NULL;
5442
      }
5443
    }
3✔
5444

5445
    errno = xerrno;
5446
  }
3✔
5447

5448
  return res;
5449
}
4✔
5450

4✔
5451
off_t pr_fsio_lseek(pr_fh_t *fh, off_t offset, int whence) {
4✔
5452
  off_t res;
5453
  pr_fs_t *fs;
4✔
5454

1✔
5455
  if (fh == NULL) {
1✔
5456
    errno = EINVAL;
5457
    return -1;
5458
  }
5459

5460
  /* Find the first non-NULL custom lseek handler.  If there are none,
5461
   * use the system lseek.
3✔
5462
   */
3✔
5463
  fs = fh->fh_fs;
5464
  while (fs && fs->fs_next && !fs->lseek) {
5465
    fs = fs->fs_next;
5466
  }
3✔
5467

5468
  pr_trace_msg(trace_channel, 8, "using %s lseek() for path '%s'", fs->fs_name,
3✔
5469
    fh->fh_path);
5470
  res = (fs->lseek)(fh, fh->fh_fd, offset, whence);
3✔
5471

5472
  return res;
5473
}
7✔
5474

7✔
5475
int pr_fsio_link(const char *target_path, const char *link_path) {
7✔
5476
  int res;
5477
  pr_fs_t *target_fs, *link_fs, *fs;
7✔
5478

7✔
5479
  if (target_path == NULL ||
3✔
5480
      link_path == NULL) {
3✔
5481
    errno = EINVAL;
5482
    return -1;
5483
  }
4✔
5484

4✔
5485
  target_fs = lookup_file_fs(target_path, NULL, FSIO_FILE_LINK);
5486
  if (target_fs == NULL) {
5487
    return -1;
5488
  }
4✔
5489

4✔
5490
  link_fs = lookup_file_fs(link_path, NULL, FSIO_FILE_LINK);
5491
  if (link_fs == NULL) {
5492
    return -1;
5493
  }
4✔
5494

4✔
5495
  if (target_fs->allow_xdev_link == FALSE ||
×
5496
      link_fs->allow_xdev_link == FALSE) {
×
5497
    if (target_fs != link_fs) {
×
5498
      errno = EXDEV;
5499
      return -1;
5500
    }
5501
  }
5502

5503
  fs = link_fs;
5504

5505
  /* Find the first non-NULL custom link handler.  If there are none,
5506
   * use the system link.
4✔
5507
   */
5508
  while (fs && fs->fs_next && !fs->link) {
5509
    fs = fs->fs_next;
5510
  }
4✔
5511

5512
  pr_trace_msg(trace_channel, 8, "using %s link() for paths '%s', '%s'",
4✔
5513
    fs->fs_name, target_path, link_path);
4✔
5514
  res = (fs->link)(fs, target_path, link_path);
1✔
5515
  if (res == 0) {
5516
    pr_fs_clear_cache2(link_path);
5517
  }
5518

5519
  return res;
5520
}
12✔
5521

12✔
5522
int pr_fsio_symlink(const char *target_path, const char *link_path) {
12✔
5523
  int res;
5524
  pr_fs_t *fs;
12✔
5525

12✔
5526
  if (target_path == NULL ||
3✔
5527
      link_path == NULL) {
3✔
5528
    errno = EINVAL;
5529
    return -1;
5530
  }
9✔
5531

9✔
5532
  fs = lookup_file_fs(link_path, NULL, FSIO_FILE_SYMLINK);
5533
  if (fs == NULL) {
5534
    return -1;
5535
  }
5536

5537
  /* Find the first non-NULL custom symlink handler.  If there are none,
5538
   * use the system symlink.
9✔
5539
   */
5540
  while (fs && fs->fs_next && !fs->symlink) {
5541
    fs = fs->fs_next;
5542
  }
9✔
5543

5544
  pr_trace_msg(trace_channel, 8, "using %s symlink() for path '%s'",
9✔
5545
    fs->fs_name, link_path);
9✔
5546
  res = (fs->symlink)(fs, target_path, link_path);
6✔
5547
  if (res == 0) {
5548
    pr_fs_clear_cache2(link_path);
5549
  }
5550

5551
  return res;
5552
}
3✔
5553

3✔
5554
int pr_fsio_ftruncate(pr_fh_t *fh, off_t len) {
3✔
5555
  int res;
5556
  pr_fs_t *fs;
3✔
5557

1✔
5558
  if (fh == NULL) {
1✔
5559
    errno = EINVAL;
5560
    return -1;
5561
  }
5562

5563
  /* Find the first non-NULL custom ftruncate handler.  If there are none,
5564
   * use the system ftruncate.
2✔
5565
   */
2✔
5566
  fs = fh->fh_fs;
5567
  while (fs && fs->fs_next && !fs->ftruncate) {
5568
    fs = fs->fs_next;
5569
  }
2✔
5570

5571
  pr_trace_msg(trace_channel, 8, "using %s ftruncate() for path '%s'",
2✔
5572
    fs->fs_name, fh->fh_path);
2✔
5573
  res = (fs->ftruncate)(fh, fh->fh_fd, len);
2✔
5574
  if (res == 0) {
5575
    pr_fs_clear_cache2(fh->fh_path);
5576

2✔
5577
    /* Clear any read buffer. */
1✔
5578
    if (fh->fh_buf != NULL) {
1✔
5579
      fh->fh_buf->current = fh->fh_buf->buf;
5580
      fh->fh_buf->remaining = fh->fh_buf->buflen;
5581
    }
5582
  }
5583

5584
  return res;
5585
}
7✔
5586

7✔
5587
int pr_fsio_truncate(const char *path, off_t len) {
7✔
5588
  int res;
5589
  pr_fs_t *fs;
7✔
5590

1✔
5591
  if (path == NULL) {
1✔
5592
    errno = EINVAL;
5593
    return -1;
5594
  }
6✔
5595

6✔
5596
  fs = lookup_file_fs(path, NULL, FSIO_FILE_TRUNC);
5597
  if (fs == NULL) {
5598
    return -1;
5599
  }
5600

5601
  /* Find the first non-NULL custom truncate handler.  If there are none,
5602
   * use the system truncate.
6✔
5603
   */
5604
  while (fs && fs->fs_next && !fs->truncate) {
5605
    fs = fs->fs_next;
5606
  }
6✔
5607

5608
  pr_trace_msg(trace_channel, 8, "using %s truncate() for path '%s'",
6✔
5609
    fs->fs_name, path);
6✔
5610
  res = (fs->truncate)(fs, path, len);
3✔
5611
  if (res == 0) {
5612
    pr_fs_clear_cache2(path);
5613
  }
5614

5615
  return res;
5616
}
8✔
5617

8✔
5618
int pr_fsio_chmod(const char *name, mode_t mode) {
8✔
5619
  int res;
5620
  pr_fs_t *fs;
8✔
5621

1✔
5622
  if (name == NULL) {
1✔
5623
    errno = EINVAL;
5624
    return -1;
5625
  }
7✔
5626

7✔
5627
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHMOD);
5628
  if (fs == NULL) {
5629
    return -1;
5630
  }
5631

5632
  /* Find the first non-NULL custom chmod handler.  If there are none,
5633
   * use the system chmod.
7✔
5634
   */
5635
  while (fs && fs->fs_next && !fs->chmod) {
5636
    fs = fs->fs_next;
5637
  }
7✔
5638

5639
  pr_trace_msg(trace_channel, 8, "using %s chmod() for path '%s'",
7✔
5640
    fs->fs_name, name);
7✔
5641
  res = (fs->chmod)(fs, name, mode);
1✔
5642
  if (res == 0) {
5643
    pr_fs_clear_cache2(name);
5644
  }
5645

5646
  return res;
5647
}
3✔
5648

5649
int pr_fsio_chmod_with_error(pool *p, const char *path, mode_t mode,
3✔
5650
    pr_error_t **err) {
5651
  int res;
3✔
5652

3✔
5653
  res = pr_fsio_chmod(path, mode);
3✔
5654
  if (res < 0) {
5655
    int xerrno = errno;
3✔
5656

3✔
5657
    if (p != NULL &&
2✔
5658
        err != NULL) {
2✔
5659
      *err = pr_error_create(p, xerrno);
1✔
5660
      if (pr_error_explain_chmod(*err, path, mode) < 0) {
1✔
5661
        pr_error_destroy(*err);
5662
        *err = NULL;
5663
      }
5664
    }
3✔
5665

5666
    errno = xerrno;
5667
  }
3✔
5668

5669
  return res;
5670
}
5✔
5671

5✔
5672
int pr_fsio_fchmod(pr_fh_t *fh, mode_t mode) {
5✔
5673
  int res;
5674
  pr_fs_t *fs;
5✔
5675

4✔
5676
  if (fh == NULL) {
4✔
5677
    errno = EINVAL;
5678
    return -1;
5679
  }
5680

5681
  /* Find the first non-NULL custom fchmod handler.  If there are none, use
5682
   * the system fchmod.
1✔
5683
   */
1✔
5684
  fs = fh->fh_fs;
5685
  while (fs && fs->fs_next && !fs->fchmod) {
5686
    fs = fs->fs_next;
5687
  }
1✔
5688

5689
  pr_trace_msg(trace_channel, 8, "using %s fchmod() for path '%s'",
1✔
5690
    fs->fs_name, fh->fh_path);
1✔
5691
  res = (fs->fchmod)(fh, fh->fh_fd, mode);
1✔
5692
  if (res == 0) {
5693
    pr_fs_clear_cache2(fh->fh_path);
5694
  }
5695

5696
  return res;
5697
}
3✔
5698

5699
int pr_fsio_fchmod_with_error(pool *p, pr_fh_t *fh, mode_t mode,
3✔
5700
    pr_error_t **err) {
5701
  int res;
3✔
5702

3✔
5703
  res = pr_fsio_fchmod(fh, mode);
3✔
5704
  if (res < 0) {
5705
    int xerrno = errno;
3✔
5706

3✔
5707
    if (p != NULL &&
2✔
5708
        err != NULL) {
5709
      int fd = -1;
2✔
5710

×
5711
      if (fh != NULL) {
5712
        fd = fh->fh_fd;
5713
      }
2✔
5714

2✔
5715
      *err = pr_error_create(p, xerrno);
1✔
5716
      if (pr_error_explain_fchmod(*err, fd, mode) < 0) {
1✔
5717
        pr_error_destroy(*err);
5718
        *err = NULL;
5719
      }
5720
    }
3✔
5721

5722
    errno = xerrno;
5723
  }
3✔
5724

5725
  return res;
5726
}
8✔
5727

8✔
5728
int pr_fsio_chown(const char *name, uid_t uid, gid_t gid) {
8✔
5729
  int res;
5730
  pr_fs_t *fs;
8✔
5731

1✔
5732
  if (name == NULL) {
1✔
5733
    errno = EINVAL;
5734
    return -1;
5735
  }
7✔
5736

7✔
5737
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHOWN);
5738
  if (fs == NULL) {
5739
    return -1;
5740
  }
5741

5742
  /* Find the first non-NULL custom chown handler.  If there are none,
5743
   * use the system chown.
7✔
5744
   */
5745
  while (fs && fs->fs_next && !fs->chown) {
5746
    fs = fs->fs_next;
5747
  }
7✔
5748

5749
  pr_trace_msg(trace_channel, 8, "using %s chown() for path '%s'",
7✔
5750
    fs->fs_name, name);
7✔
5751
  res = (fs->chown)(fs, name, uid, gid);
1✔
5752
  if (res == 0) {
5753
    pr_fs_clear_cache2(name);
5754
  }
5755

5756
  return res;
5757
}
3✔
5758

5759
int pr_fsio_chown_with_error(pool *p, const char *path, uid_t uid, gid_t gid,
3✔
5760
    pr_error_t **err) {
5761
  int res;
3✔
5762

3✔
5763
  res = pr_fsio_chown(path, uid, gid);
3✔
5764
  if (res < 0) {
5765
    int xerrno = errno;
3✔
5766

3✔
5767
    if (p != NULL &&
2✔
5768
        err != NULL) {
2✔
5769
      *err = pr_error_create(p, xerrno);
1✔
5770
      if (pr_error_explain_chown(*err, path, uid, gid) < 0) {
1✔
5771
        pr_error_destroy(*err);
5772
        *err = NULL;
5773
      }
5774
    }
3✔
5775

5776
    errno = xerrno;
5777
  }
3✔
5778

5779
  return res;
5780
}
5✔
5781

5✔
5782
int pr_fsio_fchown(pr_fh_t *fh, uid_t uid, gid_t gid) {
5✔
5783
  int res;
5784
  pr_fs_t *fs;
5✔
5785

4✔
5786
  if (fh == NULL) {
4✔
5787
    errno = EINVAL;
5788
    return -1;
5789
  }
5790

5791
  /* Find the first non-NULL custom fchown handler.  If there are none, use
5792
   * the system fchown.
1✔
5793
   */
1✔
5794
  fs = fh->fh_fs;
5795
  while (fs && fs->fs_next && !fs->fchown) {
5796
    fs = fs->fs_next;
5797
  }
1✔
5798

5799
  pr_trace_msg(trace_channel, 8, "using %s fchown() for path '%s'",
1✔
5800
    fs->fs_name, fh->fh_path);
1✔
5801
  res = (fs->fchown)(fh, fh->fh_fd, uid, gid);
1✔
5802
  if (res == 0) {
5803
    pr_fs_clear_cache2(fh->fh_path);
5804
  }
5805

5806
  return res;
5807
}
3✔
5808

5809
int pr_fsio_fchown_with_error(pool *p, pr_fh_t *fh, uid_t uid, gid_t gid,
3✔
5810
    pr_error_t **err) {
5811
  int res;
3✔
5812

3✔
5813
  res = pr_fsio_fchown(fh, uid, gid);
3✔
5814
  if (res < 0) {
5815
    int xerrno = errno;
3✔
5816

3✔
5817
    if (p != NULL &&
2✔
5818
        err != NULL) {
5819
      int fd = -1;
2✔
5820

×
5821
      if (fh != NULL) {
5822
        fd = fh->fh_fd;
5823
      }
2✔
5824

2✔
5825
      *err = pr_error_create(p, xerrno);
1✔
5826
      if (pr_error_explain_fchown(*err, fd, uid, gid) < 0) {
1✔
5827
        pr_error_destroy(*err);
5828
        *err = NULL;
5829
      }
5830
    }
3✔
5831

5832
    errno = xerrno;
5833
  }
3✔
5834

5835
  return res;
5836
}
10✔
5837

10✔
5838
int pr_fsio_lchown(const char *name, uid_t uid, gid_t gid) {
10✔
5839
  int res;
5840
  pr_fs_t *fs;
10✔
5841

1✔
5842
  if (name == NULL) {
1✔
5843
    errno = EINVAL;
5844
    return -1;
5845
  }
9✔
5846

9✔
5847
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHOWN);
5848
  if (fs == NULL) {
5849
    return -1;
5850
  }
5851

5852
  /* Find the first non-NULL custom lchown handler.  If there are none,
5853
   * use the system chown.
9✔
5854
   */
5855
  while (fs && fs->fs_next && !fs->lchown) {
5856
    fs = fs->fs_next;
5857
  }
9✔
5858

5859
  pr_trace_msg(trace_channel, 8, "using %s lchown() for path '%s'",
9✔
5860
    fs->fs_name, name);
9✔
5861
  res = (fs->lchown)(fs, name, uid, gid);
3✔
5862
  if (res == 0) {
5863
    pr_fs_clear_cache2(name);
5864
  }
5865

5866
  return res;
5867
}
3✔
5868

5869
int pr_fsio_lchown_with_error(pool *p, const char *path, uid_t uid, gid_t gid,
3✔
5870
    pr_error_t **err) {
5871
  int res;
3✔
5872

3✔
5873
  res = pr_fsio_lchown(path, uid, gid);
3✔
5874
  if (res < 0) {
5875
    int xerrno = errno;
3✔
5876

3✔
5877
    if (p != NULL &&
2✔
5878
        err != NULL) {
2✔
5879
      *err = pr_error_create(p, xerrno);
1✔
5880
      if (pr_error_explain_lchown(*err, path, uid, gid) < 0) {
1✔
5881
        pr_error_destroy(*err);
5882
        *err = NULL;
5883
      }
5884
    }
3✔
5885

5886
    errno = xerrno;
5887
  }
3✔
5888

5889
  return res;
5890
}
16✔
5891

5892
int pr_fsio_access(const char *path, int mode, uid_t uid, gid_t gid,
16✔
5893
    array_header *suppl_gids) {
5894
  pr_fs_t *fs;
16✔
5895

1✔
5896
  if (path == NULL) {
1✔
5897
    errno = EINVAL;
5898
    return -1;
5899
  }
15✔
5900

15✔
5901
  fs = lookup_file_fs(path, NULL, FSIO_FILE_ACCESS);
5902
  if (fs == NULL) {
5903
    return -1;
5904
  }
5905

5906
  /* Find the first non-NULL custom access handler.  If there are none,
5907
   * use the system access.
15✔
5908
   */
5909
  while (fs && fs->fs_next && !fs->access) {
5910
    fs = fs->fs_next;
5911
  }
15✔
5912

5913
  pr_trace_msg(trace_channel, 8, "using %s access() for path '%s'",
15✔
5914
    fs->fs_name, path);
5915
  return (fs->access)(fs, path, mode, uid, gid, suppl_gids);
5916
}
5✔
5917

5918
int pr_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
5✔
5919
    array_header *suppl_gids) {
5920
  pr_fs_t *fs;
5✔
5921

1✔
5922
  if (fh == NULL) {
1✔
5923
    errno = EINVAL;
5924
    return -1;
5925
  }
5926

5927
  /* Find the first non-NULL custom faccess handler.  If there are none,
5928
   * use the system faccess.
4✔
5929
   */
4✔
5930
  fs = fh->fh_fs;
5931
  while (fs && fs->fs_next && !fs->faccess) {
5932
    fs = fs->fs_next;
5933
  }
4✔
5934

5935
  pr_trace_msg(trace_channel, 8, "using %s faccess() for path '%s'",
4✔
5936
    fs->fs_name, fh->fh_path);
5937
  return (fs->faccess)(fh, mode, uid, gid, suppl_gids);
5938
}
5✔
5939

5✔
5940
int pr_fsio_utimes(const char *path, struct timeval *tvs) {
5✔
5941
  int res;
5942
  pr_fs_t *fs;
5✔
5943

5✔
5944
  if (path == NULL ||
1✔
5945
      tvs == NULL) {
1✔
5946
    errno = EINVAL;
5947
    return -1;
5948
  }
4✔
5949

4✔
5950
  fs = lookup_file_fs(path, NULL, FSIO_FILE_UTIMES);
5951
  if (fs == NULL) {
5952
    return -1;
5953
  }
5954

5955
  /* Find the first non-NULL custom utimes handler.  If there are none,
5956
   * use the system utimes.
4✔
5957
   */
5958
  while (fs && fs->fs_next && !fs->utimes) {
5959
    fs = fs->fs_next;
5960
  }
4✔
5961

5962
  pr_trace_msg(trace_channel, 8, "using %s utimes() for path '%s'",
4✔
5963
    fs->fs_name, path);
4✔
5964
  res = (fs->utimes)(fs, path, tvs);
1✔
5965
  if (res == 0) {
5966
    pr_fs_clear_cache2(path);
5967
  }
5968

5969
  return res;
5970
}
5971

5972
/* If the utimes(2) call fails because the process UID does not match the file
5973
 * UID, then check to see if the GIDs match (and that the file has group write
5974
 * permissions).
5975
 *
5976
 * This can be alleviated in two ways: a) if mod_cap is present, enable the
5977
 * CAP_FOWNER capability for the session, or b) use root privs.
×
5978
 */
×
5979
int pr_fsio_utimes_with_root(const char *path, struct timeval *tvs) {
×
5980
  int res, xerrno, matching_gid = FALSE;
5981
  struct stat st;
×
5982

×
5983
  res = pr_fsio_utimes(path, tvs);
5984
  xerrno = errno;
×
5985

5986
  if (res == 0) {
5987
    return 0;
5988
  }
5989

×
5990
  /* We only try these workarounds for EPERM. */
5991
  if (xerrno != EPERM) {
5992
    return res;
5993
  }
×
5994

×
5995
  pr_fs_clear_cache2(path);
×
5996
  if (pr_fsio_stat(path, &st) < 0) {
×
5997
    errno = xerrno;
5998
    return -1;
5999
  }
6000

6001
  /* Be sure to check the primary and all the supplemental groups to which
6002
   * this session belongs.
×
6003
   */
6004
  if (st.st_gid == session.gid) {
6005
    matching_gid = TRUE;
×
6006

×
6007
  } else if (session.gids != NULL) {
×
6008
    register unsigned int i;
6009
    gid_t *gids;
×
6010

×
6011
    gids = session.gids->elts;
×
6012
    for (i = 0; i < session.gids->nelts; i++) {
6013
      if (st.st_gid == gids[i]) {
6014
        matching_gid = TRUE;
6015
        break;
6016
      }
6017
    }
6018
  }
×
6019

×
6020
  if (matching_gid == TRUE &&
6021
      (st.st_mode & S_IWGRP)) {
6022

×
6023
    /* Try the utimes(2) call again, this time with root privs. */
×
6024
    pr_signals_block();
×
6025
    PRIVS_ROOT
×
6026
    res = pr_fsio_utimes(path, tvs);
×
6027
    PRIVS_RELINQUISH
6028
    pr_signals_unblock();
×
6029

6030
    if (res == 0) {
6031
      return 0;
6032
    }
6033
  }
×
6034

×
6035
  errno = xerrno;
6036
  return -1;
6037
}
2✔
6038

2✔
6039
int pr_fsio_futimes(pr_fh_t *fh, struct timeval *tvs) {
2✔
6040
  int res;
6041
  pr_fs_t *fs;
2✔
6042

2✔
6043
  if (fh == NULL ||
1✔
6044
      tvs == NULL) {
1✔
6045
    errno = EINVAL;
6046
    return -1;
6047
  }
6048

6049
  /* Find the first non-NULL custom futimes handler.  If there are none,
6050
   * use the system futimes.
1✔
6051
   */
1✔
6052
  fs = fh->fh_fs;
6053
  while (fs && fs->fs_next && !fs->futimes) {
6054
    fs = fs->fs_next;
6055
  }
1✔
6056

6057
  pr_trace_msg(trace_channel, 8, "using %s futimes() for path '%s'",
1✔
6058
    fs->fs_name, fh->fh_path);
1✔
6059
  res = (fs->futimes)(fh, fh->fh_fd, tvs);
1✔
6060
  if (res == 0) {
6061
    pr_fs_clear_cache2(fh->fh_path);
6062
  }
6063

6064
  return res;
6065
}
4✔
6066

4✔
6067
int pr_fsio_fsync(pr_fh_t *fh) {
4✔
6068
  int res;
6069
  pr_fs_t *fs;
4✔
6070

1✔
6071
  if (fh == NULL) {
1✔
6072
    errno = EINVAL;
6073
    return -1;
6074
  }
6075

6076
  /* Find the first non-NULL custom fsync handler.  If there are none,
6077
   * use the system fsync.
3✔
6078
   */
3✔
6079
  fs = fh->fh_fs;
6080
  while (fs && fs->fs_next && !fs->fsync) {
6081
    fs = fs->fs_next;
6082
  }
3✔
6083

6084
  pr_trace_msg(trace_channel, 8, "using %s fsync() for path '%s'",
3✔
6085
    fs->fs_name, fh->fh_path);
3✔
6086
  res = (fs->fsync)(fh, fh->fh_fd);
3✔
6087
  if (res == 0) {
6088
    pr_fs_clear_cache2(fh->fh_path);
6089
  }
6090

6091
  return res;
6092
}
28✔
6093

28✔
6094
const char *pr_fsio_realpath(pool *p, const char *path) {
28✔
6095
  const char *res;
6096
  pr_fs_t *fs;
28✔
6097

28✔
6098
  if (p == NULL ||
2✔
6099
      path == NULL) {
2✔
6100
    errno = EINVAL;
6101
    return NULL;
6102
  }
26✔
6103

26✔
6104
  fs = lookup_file_fs(path, NULL, FSIO_FILE_REALPATH);
6105
  if (fs == NULL) {
6106
    return NULL;
6107
  }
6108

6109
  /* Find the first non-NULL custom realpath handler.  If there are none,
6110
   * use the system realpath
26✔
6111
   */
6112
  while (fs && fs->fs_next && !fs->realpath) {
6113
    fs = fs->fs_next;
6114
  }
26✔
6115

6116
  pr_trace_msg(trace_channel, 8, "using %s realpath() for path '%s'",
26✔
6117
    fs->fs_name, path);
6118
  res = (fs->realpath)(fs, p, path);
26✔
6119

6120
  return res;
6121
}
5✔
6122

6123
ssize_t pr_fsio_getxattr(pool *p, const char *path, const char *name, void *val,
5✔
6124
    size_t valsz) {
5✔
6125
  ssize_t res;
6126
  pr_fs_t *fs;
5✔
6127

5✔
6128
  if (p == NULL ||
6129
      path == NULL ||
3✔
6130
      name == NULL) {
3✔
6131
    errno = EINVAL;
6132
    return -1;
6133
  }
2✔
6134

1✔
6135
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6136
    errno = ENOSYS;
6137
    return -1;
6138
  }
1✔
6139

1✔
6140
  fs = lookup_file_fs(path, NULL, FSIO_FILE_GETXATTR);
6141
  if (fs == NULL) {
6142
    return -1;
6143
  }
6144

6145
  /* Find the first non-NULL custom getxattr handler.  If there are none,
6146
   * use the system getxattr.
1✔
6147
   */
6148
  while (fs && fs->fs_next && !fs->getxattr) {
6149
    fs = fs->fs_next;
6150
  }
1✔
6151

6152
  pr_trace_msg(trace_channel, 8, "using %s getxattr() for path '%s'",
1✔
6153
    fs->fs_name, path);
1✔
6154
  res = (fs->getxattr)(p, fs, path, name, val, valsz);
6155
  return res;
6156
}
5✔
6157

6158
ssize_t pr_fsio_lgetxattr(pool *p, const char *path, const char *name,
5✔
6159
    void *val, size_t valsz) {
5✔
6160
  ssize_t res;
6161
  pr_fs_t *fs;
5✔
6162

5✔
6163
  if (p == NULL ||
6164
      path == NULL ||
3✔
6165
      name == NULL) {
3✔
6166
    errno = EINVAL;
6167
    return -1;
6168
  }
2✔
6169

1✔
6170
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6171
    errno = ENOSYS;
6172
    return -1;
6173
  }
1✔
6174

1✔
6175
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LGETXATTR);
6176
  if (fs == NULL) {
6177
    return -1;
6178
  }
6179

6180
  /* Find the first non-NULL custom lgetxattr handler.  If there are none,
6181
   * use the system lgetxattr.
1✔
6182
   */
6183
  while (fs && fs->fs_next && !fs->lgetxattr) {
6184
    fs = fs->fs_next;
6185
  }
1✔
6186

6187
  pr_trace_msg(trace_channel, 8, "using %s lgetxattr() for path '%s'",
1✔
6188
    fs->fs_name, path);
1✔
6189
  res = (fs->lgetxattr)(p, fs, path, name, val, valsz);
6190
  return res;
6191
}
5✔
6192

6193
ssize_t pr_fsio_fgetxattr(pool *p, pr_fh_t *fh, const char *name, void *val,
5✔
6194
    size_t valsz) {
5✔
6195
  ssize_t res;
6196
  pr_fs_t *fs;
5✔
6197

5✔
6198
  if (p == NULL ||
6199
      fh == NULL ||
3✔
6200
      name == NULL) {
3✔
6201
    errno = EINVAL;
6202
    return -1;
6203
  }
2✔
6204

1✔
6205
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6206
    errno = ENOSYS;
6207
    return -1;
6208
  }
6209

6210
  /* Find the first non-NULL custom fgetxattr handler.  If there are none,
6211
   * use the system fgetxattr.
1✔
6212
   */
1✔
6213
  fs = fh->fh_fs;
6214
  while (fs && fs->fs_next && !fs->fgetxattr) {
6215
    fs = fs->fs_next;
6216
  }
1✔
6217

6218
  pr_trace_msg(trace_channel, 8, "using %s fgetxattr() for path '%s'",
1✔
6219
    fs->fs_name, fh->fh_path);
1✔
6220
  res = (fs->fgetxattr)(p, fh, fh->fh_fd, name, val, valsz);
6221
  return res;
6222
}
7✔
6223

7✔
6224
int pr_fsio_listxattr(pool *p, const char *path, array_header **names) {
7✔
6225
  int res;
6226
  pr_fs_t *fs;
7✔
6227

7✔
6228
  if (p == NULL ||
6229
      path == NULL ||
3✔
6230
      names == NULL) {
3✔
6231
    errno = EINVAL;
6232
    return -1;
6233
  }
4✔
6234

1✔
6235
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6236
    errno = ENOSYS;
6237
    return -1;
6238
  }
3✔
6239

3✔
6240
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LISTXATTR);
6241
  if (fs == NULL) {
6242
    return -1;
6243
  }
6244

6245
  /* Find the first non-NULL custom listxattr handler.  If there are none,
6246
   * use the system listxattr.
3✔
6247
   */
6248
  while (fs && fs->fs_next && !fs->listxattr) {
6249
    fs = fs->fs_next;
6250
  }
3✔
6251

6252
  pr_trace_msg(trace_channel, 8, "using %s listxattr() for path '%s'",
3✔
6253
    fs->fs_name, path);
3✔
6254
  res = (fs->listxattr)(p, fs, path, names);
6255
  return res;
6256
}
5✔
6257

5✔
6258
int pr_fsio_llistxattr(pool *p, const char *path, array_header **names) {
5✔
6259
  int res;
6260
  pr_fs_t *fs;
5✔
6261

5✔
6262
  if (p == NULL ||
6263
      path == NULL ||
3✔
6264
      names == NULL) {
3✔
6265
    errno = EINVAL;
6266
    return -1;
6267
  }
2✔
6268

1✔
6269
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6270
    errno = ENOSYS;
6271
    return -1;
6272
  }
1✔
6273

1✔
6274
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LLISTXATTR);
6275
  if (fs == NULL) {
6276
    return -1;
6277
  }
6278

6279
  /* Find the first non-NULL custom llistxattr handler.  If there are none,
6280
   * use the system llistxattr.
1✔
6281
   */
6282
  while (fs && fs->fs_next && !fs->llistxattr) {
6283
    fs = fs->fs_next;
6284
  }
1✔
6285

6286
  pr_trace_msg(trace_channel, 8, "using %s llistxattr() for path '%s'",
1✔
6287
    fs->fs_name, path);
1✔
6288
  res = (fs->llistxattr)(p, fs, path, names);
6289
  return res;
6290
}
7✔
6291

7✔
6292
int pr_fsio_flistxattr(pool *p, pr_fh_t *fh, array_header **names) {
7✔
6293
  int res;
6294
  pr_fs_t *fs;
7✔
6295

7✔
6296
  if (p == NULL ||
6297
      fh == NULL ||
3✔
6298
      names == NULL) {
3✔
6299
    errno = EINVAL;
6300
    return -1;
6301
  }
4✔
6302

1✔
6303
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6304
    errno = ENOSYS;
6305
    return -1;
6306
  }
6307

6308
  /* Find the first non-NULL custom flistxattr handler.  If there are none,
6309
   * use the system flistxattr.
3✔
6310
   */
3✔
6311
  fs = fh->fh_fs;
6312
  while (fs && fs->fs_next && !fs->flistxattr) {
6313
    fs = fs->fs_next;
6314
  }
3✔
6315

6316
  pr_trace_msg(trace_channel, 8, "using %s flistxattr() for path '%s'",
3✔
6317
    fs->fs_name, fh->fh_path);
3✔
6318
  res = (fs->flistxattr)(p, fh, fh->fh_fd, names);
6319
  return res;
6320
}
5✔
6321

5✔
6322
int pr_fsio_removexattr(pool *p, const char *path, const char *name) {
5✔
6323
  int res;
6324
  pr_fs_t *fs;
5✔
6325

5✔
6326
  if (p == NULL ||
6327
      path == NULL ||
3✔
6328
      name == NULL) {
3✔
6329
    errno = EINVAL;
6330
    return -1;
6331
  }
2✔
6332

1✔
6333
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6334
    errno = ENOSYS;
6335
    return -1;
6336
  }
1✔
6337

1✔
6338
  fs = lookup_file_fs(path, NULL, FSIO_FILE_REMOVEXATTR);
6339
  if (fs == NULL) {
6340
    return -1;
6341
  }
6342

6343
  /* Find the first non-NULL custom removexattr handler.  If there are none,
6344
   * use the system removexattr.
1✔
6345
   */
6346
  while (fs && fs->fs_next && !fs->removexattr) {
6347
    fs = fs->fs_next;
6348
  }
1✔
6349

6350
  pr_trace_msg(trace_channel, 8, "using %s removexattr() for path '%s'",
1✔
6351
    fs->fs_name, path);
1✔
6352
  res = (fs->removexattr)(p, fs, path, name);
6353
  return res;
6354
}
5✔
6355

5✔
6356
int pr_fsio_lremovexattr(pool *p, const char *path, const char *name) {
5✔
6357
  int res;
6358
  pr_fs_t *fs;
5✔
6359

5✔
6360
  if (p == NULL ||
6361
      path == NULL ||
3✔
6362
      name == NULL) {
3✔
6363
    errno = EINVAL;
6364
    return -1;
6365
  }
2✔
6366

1✔
6367
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6368
    errno = ENOSYS;
6369
    return -1;
6370
  }
1✔
6371

1✔
6372
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LREMOVEXATTR);
6373
  if (fs == NULL) {
6374
    return -1;
6375
  }
6376

6377
  /* Find the first non-NULL custom lremovexattr handler.  If there are none,
6378
   * use the system lremovexattr.
1✔
6379
   */
6380
  while (fs && fs->fs_next && !fs->lremovexattr) {
6381
    fs = fs->fs_next;
6382
  }
1✔
6383

6384
  pr_trace_msg(trace_channel, 8, "using %s lremovexattr() for path '%s'",
1✔
6385
    fs->fs_name, path);
1✔
6386
  res = (fs->lremovexattr)(p, fs, path, name);
6387
  return res;
6388
}
5✔
6389

5✔
6390
int pr_fsio_fremovexattr(pool *p, pr_fh_t *fh, const char *name) {
5✔
6391
  int res;
6392
  pr_fs_t *fs;
5✔
6393

5✔
6394
  if (p == NULL ||
6395
      fh == NULL ||
3✔
6396
      name == NULL) {
3✔
6397
    errno = EINVAL;
6398
    return -1;
6399
  }
2✔
6400

1✔
6401
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6402
    errno = ENOSYS;
6403
    return -1;
6404
  }
6405

6406
  /* Find the first non-NULL custom fremovexattr handler.  If there are none,
6407
   * use the system fremovexattr.
1✔
6408
   */
1✔
6409
  fs = fh->fh_fs;
6410
  while (fs && fs->fs_next && !fs->fremovexattr) {
6411
    fs = fs->fs_next;
6412
  }
1✔
6413

6414
  pr_trace_msg(trace_channel, 8, "using %s fremovexattr() for path '%s'",
1✔
6415
    fs->fs_name, fh->fh_path);
1✔
6416
  res = (fs->fremovexattr)(p, fh, fh->fh_fd, name);
6417
  return res;
6418
}
6✔
6419

6420
int pr_fsio_setxattr(pool *p, const char *path, const char *name, void *val,
6✔
6421
    size_t valsz, int flags) {
6✔
6422
  int res;
6423
  pr_fs_t *fs;
6✔
6424

6✔
6425
  if (p == NULL ||
6426
      path == NULL ||
3✔
6427
      name == NULL) {
3✔
6428
    errno = EINVAL;
6429
    return -1;
6430
  }
3✔
6431

1✔
6432
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6433
    errno = ENOSYS;
6434
    return -1;
6435
  }
2✔
6436

2✔
6437
  fs = lookup_file_fs(path, NULL, FSIO_FILE_SETXATTR);
6438
  if (fs == NULL) {
6439
    return -1;
6440
  }
6441

6442
  /* Find the first non-NULL custom setxattr handler.  If there are none,
6443
   * use the system setxattr.
2✔
6444
   */
6445
  while (fs && fs->fs_next && !fs->setxattr) {
6446
    fs = fs->fs_next;
6447
  }
2✔
6448

6449
  pr_trace_msg(trace_channel, 8, "using %s setxattr() for path '%s'",
2✔
6450
    fs->fs_name, path);
2✔
6451
  res = (fs->setxattr)(p, fs, path, name, val, valsz, flags);
6452
  return res;
6453
}
6✔
6454

6455
int pr_fsio_lsetxattr(pool *p, const char *path, const char *name, void *val,
6✔
6456
    size_t valsz, int flags) {
6✔
6457
  int res;
6458
  pr_fs_t *fs;
6✔
6459

6✔
6460
  if (p == NULL ||
6461
      path == NULL ||
3✔
6462
      name == NULL) {
3✔
6463
    errno = EINVAL;
6464
    return -1;
6465
  }
3✔
6466

1✔
6467
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6468
    errno = ENOSYS;
6469
    return -1;
6470
  }
2✔
6471

2✔
6472
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LSETXATTR);
6473
  if (fs == NULL) {
6474
    return -1;
6475
  }
6476

6477
  /* Find the first non-NULL custom lsetxattr handler.  If there are none,
6478
   * use the system lsetxattr.
2✔
6479
   */
6480
  while (fs && fs->fs_next && !fs->lsetxattr) {
6481
    fs = fs->fs_next;
6482
  }
2✔
6483

6484
  pr_trace_msg(trace_channel, 8, "using %s lsetxattr() for path '%s'",
2✔
6485
    fs->fs_name, path);
2✔
6486
  res = (fs->lsetxattr)(p, fs, path, name, val, valsz, flags);
6487
  return res;
6488
}
5✔
6489

6490
int pr_fsio_fsetxattr(pool *p, pr_fh_t *fh, const char *name, void *val,
5✔
6491
    size_t valsz, int flags) {
5✔
6492
  int res;
6493
  pr_fs_t *fs;
5✔
6494

5✔
6495
  if (p == NULL ||
6496
      fh == NULL ||
3✔
6497
      name == NULL) {
3✔
6498
    errno = EINVAL;
6499
    return -1;
6500
  }
2✔
6501

1✔
6502
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6503
    errno = ENOSYS;
6504
    return -1;
6505
  }
6506

6507
  /* Find the first non-NULL custom fsetxattr handler.  If there are none,
6508
   * use the system fsetxattr.
1✔
6509
   */
1✔
6510
  fs = fh->fh_fs;
6511
  while (fs && fs->fs_next && !fs->fsetxattr) {
6512
    fs = fs->fs_next;
6513
  }
1✔
6514

6515
  pr_trace_msg(trace_channel, 8, "using %s fsetxattr() for path '%s'",
1✔
6516
    fs->fs_name, fh->fh_path);
1✔
6517
  res = (fs->fsetxattr)(p, fh, fh->fh_fd, name, val, valsz, flags);
6518
  return res;
6519
}
6520

6521
/* If the wrapped chroot() function succeeds (e.g. returns 0), then all
6522
 * pr_fs_ts currently registered in the fs_map will have their paths
6523
 * rewritten to reflect the new root.
9✔
6524
 */
9✔
6525
int pr_fsio_chroot(const char *path) {
9✔
6526
  int res = 0, xerrno = 0;
6527
  pr_fs_t *fs;
9✔
6528

1✔
6529
  if (path == NULL) {
1✔
6530
    errno = EINVAL;
6531
    return -1;
6532
  }
8✔
6533

8✔
6534
  fs = lookup_dir_fs(path, FSIO_DIR_CHROOT);
6535
  if (fs == NULL) {
6536
    return -1;
6537
  }
6538

6539
  /* Find the first non-NULL custom chroot handler.  If there are none,
6540
   * use the system chroot.
4✔
6541
   */
6542
  while (fs && fs->fs_next && !fs->chroot) {
6543
    fs = fs->fs_next;
6544
  }
4✔
6545

6546
  pr_trace_msg(trace_channel, 8, "using %s chroot() for path '%s'",
4✔
6547
    fs->fs_name, path);
4✔
6548
  res = (fs->chroot)(fs, path);
6549
  xerrno = errno;
4✔
6550

1✔
6551
  if (res == 0) {
6552
    unsigned int iter_start = 0;
6553

6554
    /* The filesystem handles in fs_map need to be readjusted to the new root.
1✔
6555
     */
1✔
6556
    register unsigned int i = 0;
1✔
6557
    pool *map_pool = make_sub_pool(permanent_pool);
1✔
6558
    array_header *new_map = make_array(map_pool, 0, sizeof(pr_fs_t *));
6559
    pr_fs_t **fs_objs = NULL;
1✔
6560

6561
    pr_pool_tag(map_pool, "FSIO Map Pool");
1✔
6562

1✔
6563
    if (fs_map) {
6564
      fs_objs = (pr_fs_t **) fs_map->elts;
6565
    }
1✔
6566

1✔
6567
    if (fs != root_fs) {
1✔
6568
      if (strncmp(fs->fs_path, path, strlen(path)) == 0) {
×
6569
        memmove(fs->fs_path, fs->fs_path + strlen(path),
6570
          strlen(fs->fs_path) - strlen(path) + 1);
6571
      }
1✔
6572

1✔
6573
      *((pr_fs_t **) push_array(new_map)) = fs;
6574
      iter_start = 1;
6575
    }
1✔
6576

×
6577
    for (i = iter_start; i < (fs_map ? fs_map->nelts : 0); i++) {
6578
      pr_fs_t *tmpfs = fs_objs[i];
6579

6580
      /* The memory for this field has already been allocated, so futzing
6581
       * with it like this should be fine.  Watch out for any paths that
6582
       * may be different, e.g. added manually, not through pr_register_fs().
6583
       * Any absolute paths that are outside of the chroot path are discarded.
6584
       * Deferred-resolution paths (eg "~" paths) and relative paths are kept.
6585
       */
×
6586

×
6587
      if (strncmp(tmpfs->fs_path, path, strlen(path)) == 0) {
6588
        pr_fs_t *next;
×
6589

×
6590
        memmove(tmpfs->fs_path, tmpfs->fs_path + strlen(path),
6591
          strlen(tmpfs->fs_path) - strlen(path) + 1);
6592

×
6593
        /* Need to do this for any stacked FSs as well. */
×
6594
        next = tmpfs->fs_next;
×
6595
        while (next != NULL) {
6596
          pr_signals_handle();
×
6597

×
6598
          memmove(next->fs_path, next->fs_path + strlen(path),
6599
            strlen(next->fs_path) - strlen(path) + 1);
×
6600

6601
          next = next->fs_next;
6602
        }
6603
      }
6604

×
6605
      /* Add this FS to the new fs_map. */
6606
      *((pr_fs_t **) push_array(new_map)) = tmpfs;
6607
    }
6608

1✔
6609
    /* Sort the new map */
6610
    qsort(new_map->elts, new_map->nelts, sizeof(pr_fs_t *), fs_cmp);
6611

1✔
6612
    /* Destroy the old map */
1✔
6613
    if (fs_map != NULL) {
6614
      destroy_pool(fs_map->pool);
6615
    }
1✔
6616

1✔
6617
    fs_map = new_map;
6618
    chk_fs_map = TRUE;
6619
  }
4✔
6620

4✔
6621
  errno = xerrno;
6622
  return res;
6623
}
7✔
6624

7✔
6625
int pr_fsio_chroot_with_error(pool *p, const char *path, pr_error_t **err) {
6626
  int res;
7✔
6627

7✔
6628
  res = pr_fsio_chroot(path);
7✔
6629
  if (res < 0) {
6630
    int xerrno = errno;
7✔
6631

7✔
6632
    if (p != NULL &&
6✔
6633
        err != NULL) {
6✔
6634
      *err = pr_error_create(p, xerrno);
5✔
6635
      if (pr_error_explain_chroot(*err, path) < 0) {
5✔
6636
        pr_error_destroy(*err);
6637
        *err = NULL;
6638
      }
6639
    }
7✔
6640

6641
    errno = xerrno;
6642
  }
7✔
6643

6644
  return res;
6645
}
4✔
6646

4✔
6647
char *pr_fsio_getpipebuf(pool *p, int fd, long *bufsz) {
4✔
6648
  char *buf = NULL;
6649
  long buflen;
4✔
6650

1✔
6651
  if (p == NULL) {
1✔
6652
    errno = EINVAL;
6653
    return NULL;
6654
  }
3✔
6655

1✔
6656
  if (fd < 0) {
1✔
6657
    errno = EBADF;
6658
    return NULL;
6659
  }
6660

2✔
6661
#if defined(PIPE_BUF)
6662
  buflen = PIPE_BUF;
6663

6664
#elif defined(HAVE_FPATHCONF)
6665
  /* Some platforms do not define a PIPE_BUF constant.  For them, we need
6666
   * to use fpathconf(2), if available.
6667
   */
6668
  buflen = fpathconf(fd, _PC_PIPE_BUF);
6669
  if (buflen < 0) {
6670
    return NULL;
6671
  }
6672

6673
#else
6674
  errno = ENOSYS;
6675
  return NULL;
6676
#endif
2✔
6677

1✔
6678
  if (bufsz != NULL) {
6679
    *bufsz = buflen;
6680
  }
2✔
6681

2✔
6682
  buf = palloc(p, buflen);
6683
  return buf;
6684
}
63✔
6685

63✔
6686
char *pr_fsio_gets(char *buf, size_t size, pr_fh_t *fh) {
63✔
6687
  char *bp = NULL;
63✔
6688
  int toread = 0;
6689
  pr_buffer_t *pbuf = NULL;
63✔
6690

63✔
6691
  if (buf == NULL ||
6692
      fh == NULL ||
3✔
6693
      size == 0) {
3✔
6694
    errno = EINVAL;
6695
    return NULL;
6696
  }
60✔
6697

16✔
6698
  if (fh->fh_buf == NULL) {
6699
    size_t bufsz;
6700

6701
    /* Conscientious callers who want the optimal IO on the file should
6702
     * set the fh->fh_iosz hint.
16✔
6703
     */
6704
    bufsz = fh->fh_iosz ? fh->fh_iosz : PR_TUNABLE_BUFFER_SIZE;
16✔
6705

16✔
6706
    fh->fh_buf = pcalloc(fh->fh_pool, sizeof(pr_buffer_t));
16✔
6707
    fh->fh_buf->buf = fh->fh_buf->current = pcalloc(fh->fh_pool, bufsz);
6708
    fh->fh_buf->remaining = fh->fh_buf->buflen = bufsz;
6709
  }
60✔
6710

60✔
6711
  pbuf = fh->fh_buf;
6712
  bp = buf;
60✔
6713

60✔
6714
  while (size) {
6715
    pr_signals_handle();
60✔
6716

60✔
6717
    if (pbuf->current == NULL ||
6718
        pbuf->remaining == pbuf->buflen) { /* empty buffer */
30✔
6719

30✔
6720
      toread = pr_fsio_read(fh, pbuf->buf, pbuf->buflen);
14✔
6721
      if (toread <= 0) {
×
6722
        if (bp != buf) {
×
6723
          *bp = '\0';
6724
          return buf;
6725
        }
6726

6727
        return NULL;
6728
      }
16✔
6729

16✔
6730
      pbuf->remaining = pbuf->buflen - toread;
6731
      pbuf->current = pbuf->buf;
6732

30✔
6733
    } else {
6734
      toread = pbuf->buflen - pbuf->remaining;
6735
    }
6736

6737
    /* TODO: Improve the efficiency of this copy by using a strnchr(3)
6738
     * scan to find the next LF, and then a memmove(2) to do the copy.
967✔
6739
     */
1,934✔
6740
    while (size &&
967✔
6741
           toread > 0 &&
921✔
6742
           *pbuf->current != '\n' &&
921✔
6743
           toread--) {
6744
      pr_signals_handle();
921✔
6745

921✔
6746
      *bp++ = *pbuf->current++;
921✔
6747
      size--;
6748
      pbuf->remaining++;
6749
    }
46✔
6750

46✔
6751
    if (size &&
46✔
6752
        toread &&
46✔
6753
        *pbuf->current == '\n') {
46✔
6754
      size--;
46✔
6755
      toread--;
46✔
6756
      *bp++ = *pbuf->current++;
46✔
6757
      pbuf->remaining++;
6758
      break;
6759
    }
×
6760

×
6761
    if (!toread) {
6762
      pbuf->current = NULL;
6763
    }
6764
  }
46✔
6765

46✔
6766
  *bp = '\0';
6767
  return buf;
6768
}
6769

6770
/* pr_fsio_getline() is an fgets() with backslash-newline stripping, copied from
6771
 * Wietse Venema's tcpwrapppers-7.6 code.  The extra *lineno argument is
6772
 * needed, at the moment, to properly track which line of the configuration
6773
 * file is being read in, so that errors can be reported with line numbers
6774
 * correctly.
21✔
6775
 */
6776
char *pr_fsio_getline(char *buf, size_t buflen, pr_fh_t *fh,
21✔
6777
    unsigned int *lineno) {
21✔
6778
  int inlen;
6779
  char *start;
21✔
6780

21✔
6781
  if (buf == NULL ||
6782
      fh == NULL ||
3✔
6783
      buflen == 0) {
3✔
6784
    errno = EINVAL;
6785
    return NULL;
6786
  }
6787

19✔
6788
  start = buf;
13✔
6789
  while (pr_fsio_gets(buf, buflen, fh) != NULL) {
6790
    pr_signals_handle();
13✔
6791

6792
    inlen = strlen(buf);
13✔
6793

13✔
6794
    if (inlen >= 1) {
13✔
6795
      if (buf[inlen - 1] == '\n') {
13✔
6796
        if (lineno != NULL) {
6797
          (*lineno)++;
6798
        }
13✔
6799

1✔
6800
        if (inlen >= 2 && buf[inlen - 2] == '\\') {
6801
          char *bufp;
1✔
6802

6803
          inlen -= 2;
6804

6805
          /* Watch for commented lines when handling line continuations.
6806
           * Advance past any leading whitespace, to see if the first
6807
           * non-whitespace character is the comment character.
1✔
6808
           */
×
6809
          for (bufp = buf; *bufp && PR_ISSPACE(*bufp); bufp++) {
6810
          }
1✔
6811

×
6812
          if (*bufp == '#') {
6813
            continue;
6814
          }
6815

6816
        } else {
6817
          return start;
6818
        }
6819
      }
6820
    }
6821

1✔
6822
    /* Be careful of reading too much. */
×
6823
    if (buflen - inlen == 0) {
6824
      return buf;
6825
    }
1✔
6826

1✔
6827
    buf += inlen;
1✔
6828
    buflen -= inlen;
6829
    buf[0] = 0;
6830
  }
6✔
6831

6832
  return (buf > start ? start : NULL);
6833
}
6834

6835
#define FSIO_MAX_FD_COUNT                1024
×
6836

×
6837
void pr_fs_close_extra_fds(void) {
×
6838
  register unsigned int i;
×
6839
  long nfiles = 0;
6840
  struct rlimit rlim;
6841

6842
  /* Close any but the big three open fds.
6843
   *
6844
   * First, use getrlimit() to obtain the maximum number of open files
6845
   * for this process -- then close that number.
6846
   */
6847
#if defined(RLIMIT_NOFILE) || defined(RLIMIT_OFILE)
×
6848
# if defined(RLIMIT_NOFILE)
6849
  if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
6850
# elif defined(RLIMIT_OFILE)
6851
  if (getrlimit(RLIMIT_OFILE, &rlim) < 0) {
6852
# endif
6853
    /* Ignore ENOSYS (and EPERM, since some libc's use this as ENOSYS); pick
6854
     * some arbitrary high number.
6855
     */
6856
    nfiles = FSIO_MAX_FD_COUNT;
6857

×
6858
  } else {
6859
    nfiles = rlim.rlim_max;
6860
  }
6861

6862
#else /* no RLIMIT_NOFILE or RLIMIT_OFILE */
6863
   nfiles = FSIO_MAX_FD_COUNT;
6864
#endif
6865

6866
  /* Yes, using a long for the nfiles variable is not quite kosher; it should
6867
   * be an unsigned type, otherwise a large limit (say, RLIMIT_INFINITY)
6868
   * might overflow the data type.  In that case, though, we want to know
6869
   * about it -- and using a signed type, we will know if the overflowed
6870
   * value is a negative number.  Chances are we do NOT want to be closing
6871
   * fds whose value is as high as they can possibly get; that's too many
6872
   * fds to iterate over.  Long story short, using a long int is just fine.
6873
   * (Plus it makes mod_exec work on Mac OSX 10.4; without this tweak,
6874
   * mod_exec's forked processes never return/exit.)
6875
   */
×
6876

6877
  if (nfiles < 0 ||
×
6878
      nfiles > FSIO_MAX_FD_COUNT) {
6879
    nfiles = FSIO_MAX_FD_COUNT;
6880
  }
6881

×
6882
  /* Close the "non-standard" file descriptors. */
6883
  for (i = 3; i < nfiles; i++) {
×
6884
    /* This is a potentially long-running loop, so handle signals. */
×
6885
    pr_signals_handle();
6886
    (void) close((int) i);
×
6887
  }
6888
}
6889

6890
/* Be generous in the maximum allowed number of dup fds, in our search for
6891
 * one that is outside the big three.
6892
 *
6893
 * In theory, this should be a runtime lookup using getdtablesize(2), being
6894
 * sure to handle the ENOSYS case (for older systems).
6895
 */
6896
#define FSIO_MAX_DUPFDS                512
6897

6898
/* The main three fds (stdin, stdout, stderr) need to be protected, reserved
6899
 * for use.  This function uses dup(2) to open new fds on the given fd
6900
 * until the new fd is not one of the big three.
5✔
6901
 */
5✔
6902
int pr_fs_get_usable_fd(int fd) {
5✔
6903
  register int i;
6904
  int fdi, dup_fds[FSIO_MAX_DUPFDS], n;
5✔
6905

6906
  if (fd > STDERR_FILENO) {
6907
    return fd;
6908
  }
4✔
6909

4✔
6910
  memset(dup_fds, -1, sizeof(dup_fds));
4✔
6911
  i = 0;
6912
  n = -1;
4✔
6913

4✔
6914
  fdi = fd;
4✔
6915
  while (i < FSIO_MAX_DUPFDS) {
6916
    pr_signals_handle();
4✔
6917

4✔
6918
    dup_fds[i] = dup(fdi);
2✔
6919
    if (dup_fds[i] < 0) {
2✔
6920
      register int j;
6921
      int xerrno  = errno;
6922

4✔
6923
      /* Need to clean up any previously opened dups as well. */
2✔
6924
      for (j = 0; j <= i; j++) {
2✔
6925
        close(dup_fds[j]);
6926
        dup_fds[j] = -1;
6927
      }
2✔
6928

2✔
6929
      errno = xerrno;
6930
      return -1;
6931
    }
2✔
6932

6933
    if (dup_fds[i] <= STDERR_FILENO) {
×
6934
      /* Continue searching for an open fd that isn't 0, 1, or 2. */
×
6935
      fdi = dup_fds[i];
×
6936
      i++;
6937
      continue;
6938
    }
6939

6940
    n = i;
6941
    fdi = dup_fds[n];
6942
    break;
6943
  }
6944

6945
  /* If n is -1, we reached the max number of dups without finding an
6946
   * open one.  Hard to imagine this happening, but catch the case anyway.
2✔
6947
   */
6948
  if (n == -1) {
×
6949
    /* Free up the fds we opened in our search. */
×
6950
    for (i = 0; i < FSIO_MAX_DUPFDS; i++) {
×
6951
      if (dup_fds[i] >= 0) {
×
6952
        close(dup_fds[i]);
6953
        dup_fds[i] = -1;
6954
      }
6955
    }
×
6956

×
6957
    errno = EPERM;
6958
    return -1;
6959
  }
6960

2✔
6961
  /* Free up the fds we opened in our search. */
×
6962
  for (i = 0; i < n; i++) {
×
6963
    (void) close(dup_fds[i]);
6964
    dup_fds[i] = -1;
6965
  }
6966

6967
  return fdi;
6968
}
40✔
6969

40✔
6970
int pr_fs_get_usable_fd2(int *fd) {
6971
  int new_fd = -1, res = 0;
40✔
6972

1✔
6973
  if (fd == NULL) {
1✔
6974
    errno = EINVAL;
6975
    return -1;
6976
  }
39✔
6977

6978
  if (*fd > STDERR_FILENO) {
6979
    /* No need to obtain a different fd; the given one is already not one
6980
     * of the big three.
6981
     */
6982
    return 0;
6983
  }
2✔
6984

2✔
6985
  new_fd = pr_fs_get_usable_fd(*fd);
1✔
6986
  if (new_fd >= 0) {
1✔
6987
    (void) close(*fd);
6988
    *fd = new_fd;
6989

6990
  } else {
6991
    res = -1;
6992
  }
6993

6994
  return res;
6995
}
6996

6997
/* Simple multiplication and division doesn't work with very large
6998
 * filesystems (overflows 32 bits).  This code should handle it.
6999
 *
7000
 * Note that this returns a size in KB, not bytes.
7001
 */
7002
static off_t get_fs_size(size_t nblocks, size_t blocksz) {
7003
  off_t bl_lo, bl_hi;
7004
  off_t res_lo, res_hi, tmp;
7005

7006
  bl_lo = nblocks & 0x0000ffff;
7007
  bl_hi = nblocks & 0xffff0000;
7008

7009
  tmp = (bl_hi >> 16) * blocksz;
7010
  res_hi = tmp & 0xffff0000;
7011
  res_lo = (tmp & 0x0000ffff) << 16;
7012
  res_lo += bl_lo * blocksz;
7013

7014
  if (res_hi & 0xfc000000) {
7015
    /* Overflow */
7016
    return 0;
7017
  }
7018

7019
  return (res_lo >> 10) | (res_hi << 6);
7020
}
16✔
7021

16✔
7022
static int fs_getsize(int fd, char *path, off_t *fs_size) {
7023
  int res = -1;
7024

7025
# if defined(HAVE_SYS_STATVFS_H)
7026

7027
#  if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && \
7028
    defined(SOLARIS2) && !defined(SOLARIS2_5_1) && !defined(SOLARIS2_6) && \
7029
    !defined(SOLARIS2_7)
7030
  /* Note: somewhere along the way, Sun decided that the prototype for
7031
   * its statvfs64(2) function would include a statvfs64_t rather than
7032
   * struct statvfs64.  In 2.6 and 2.7, it's struct statvfs64, and
7033
   * in 8, 9 it's statvfs64_t.  This should silence compiler warnings.
7034
   * (The statvfs_t will be redefined to a statvfs64_t as appropriate on
7035
   * LFS systems).
7036
   */
7037
  statvfs_t fs;
16✔
7038
#  else
7039
  struct statvfs fs;
7040
#  endif /* LFS && !Solaris 2.5.1 && !Solaris 2.6 && !Solaris 2.7 */
16✔
7041

3✔
7042
  if (fs_size == NULL) {
3✔
7043
    errno = EINVAL;
7044
    return -1;
7045
  }
13✔
7046

2✔
7047
  if (path != NULL) {
7048
    pr_trace_msg(trace_channel, 18, "using statvfs() on '%s'", path);
7049

11✔
7050
  } else {
7051
    pr_trace_msg(trace_channel, 18, "using statvfs() on fd %d", fd);
7052
  }
13✔
7053

2✔
7054
  if (path != NULL) {
7055
    res = statvfs(path, &fs);
7056

11✔
7057
  } else {
7058
    res = fstatvfs(fd, &fs);
7059
  }
13✔
7060

2✔
7061
  if (res < 0) {
7062
    int xerrno = errno;
2✔
7063

×
7064
    if (path != NULL) {
7065
      pr_trace_msg(trace_channel, 3, "statvfs() error using '%s': %s",
7066
        path, strerror(xerrno));
7067

2✔
7068
    } else {
7069
      pr_trace_msg(trace_channel, 3, "statvfs() error using fd %d: %s",
7070
        fd, strerror(xerrno));
7071
    }
2✔
7072

2✔
7073
    errno = xerrno;
7074
    return -1;
7075
  }
7076

7077
  /* The get_fs_size() function is only useful for 32-bit numbers;
7078
   * if either of our two values are in datatypes larger than 4 bytes,
7079
   * we'll use typecasting.
11✔
7080
   */
7081
  if (sizeof(fs.f_bavail) > 4 ||
7082
      sizeof(fs.f_frsize) > 4) {
7083

7084
    /* In order to return a size in KB, as get_fs_size() does, we need
7085
     * to divide by 1024.
11✔
7086
     */
7087
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7088

7089
  } else {
7090
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7091
  }
11✔
7092

7093
  res = 0;
7094

7095
# elif defined(HAVE_SYS_VFS_H)
7096
  struct statfs fs;
7097

7098
  if (fs_size == NULL) {
7099
    errno = EINVAL;
7100
    return -1;
7101
  }
7102

7103
  if (path != NULL) {
7104
    pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7105

7106
  } else {
7107
    pr_trace_msg(trace_channel, 18, "using statfs() on fd %d", fd);
7108
  }
7109

7110
  if (path != NULL) {
7111
    res = statfs(path, &fs);
7112

7113
  } else {
7114
    res = fstatfs(fd, &fs);
7115
  }
7116

7117
  if (res < 0) {
7118
    int xerrno = errno;
7119

7120
    if (path != NULL) {
7121
      pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7122
        path, strerror(xerrno));
7123

7124
    } else {
7125
      pr_trace_msg(trace_channel, 3, "statfs() error using fd %d: %s",
7126
        fd, strerror(xerrno));
7127
    }
7128

7129
    errno = xerrno;
7130
    return -1;
7131
  }
7132

7133
  /* The get_fs_size() function is only useful for 32-bit numbers;
7134
   * if either of our two values are in datatypes larger than 4 bytes,
7135
   * we'll use typecasting.
7136
   */
7137
  if (sizeof(fs.f_bavail) > 4 ||
7138
      sizeof(fs.f_frsize) > 4) {
7139

7140
    /* In order to return a size in KB, as get_fs_size() does, we need
7141
     * to divide by 1024.
7142
     */
7143
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7144

7145
  } else {
7146
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7147
  }
7148

7149
  res = 0;
7150

7151
# elif defined(HAVE_STATFS)
7152
  struct statfs fs;
7153

7154
  if (fs_size == NULL) {
7155
    errno = EINVAL;
7156
    return -1;
7157
  }
7158

7159
  if (path != NULL) {
7160
    pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7161

7162
  } else {
7163
    pr_trace_msg(trace_channel, 18, "using statfs() on fd %d", fd);
7164
  }
7165

7166
  if (path != NULL) {
7167
    res = statfs(path, &fs);
7168

7169
  } else {
7170
    res = fstatfs(fd, &fs);
7171
  }
7172

7173
  if (res < 0) {
7174
    int xerrno = errno;
7175

7176
    if (path != NULL) {
7177
      pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7178
        path, strerror(xerrno));
7179

7180
    } else {
7181
      pr_trace_msg(trace_channel, 3, "statfs() error using fd %d: %s",
7182
        fd, strerror(xerrno));
7183
    }
7184

7185
    errno = xerrno;
7186
    return -1;
7187
  }
7188

7189
  /* The get_fs_size() function is only useful for 32-bit numbers;
7190
   * if either of our two values are in datatypes larger than 4 bytes,
7191
   * we'll use typecasting.
7192
   */
7193
  if (sizeof(fs.f_bavail) > 4 ||
7194
      sizeof(fs.f_frsize) > 4) {
7195

7196
    /* In order to return a size in KB, as get_fs_size() does, we need
7197
     * to divide by 1024.
7198
     */
7199
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7200

7201
  } else {
7202
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7203
  }
7204

7205
  res = 0;
7206

7207
# else
7208
  errno = ENOSYS:
7209
  res = -1;
7210
# endif /* !HAVE_STATFS && !HAVE_SYS_STATVFS && !HAVE_SYS_VFS */
11✔
7211

7212
  return res;
7213
}
7214

7215
#if defined(HAVE_STATFS) || defined(HAVE_SYS_STATVFS_H) || \
2✔
7216
  defined(HAVE_SYS_VFS_H)
2✔
7217
off_t pr_fs_getsize(char *path) {
2✔
7218
  int res;
7219
  off_t fs_size;
4✔
7220

2✔
7221
  res = pr_fs_getsize2(path, &fs_size);
1✔
7222
  if (res < 0) {
1✔
7223
    errno = EINVAL;
7224
    fs_size = -1;
7225
  }
2✔
7226

7227
  return fs_size;
7228
}
7229
#endif /* !HAVE_STATFS && !HAVE_SYS_STATVFS && !HAVE_SYS_VFS */
7230

5✔
7231
/* Returns the size in KB via the `fs_size' argument. */
5✔
7232
int pr_fs_getsize2(char *path, off_t *fs_size) {
7233
  return fs_getsize(-1, path, fs_size);
7234
}
11✔
7235

11✔
7236
int pr_fs_fgetsize(int fd, off_t *fs_size) {
7237
  return fs_getsize(fd, NULL, fs_size);
7238
}
12✔
7239

7240
void pr_fs_fadvise(int fd, off_t offset, off_t len, int advice) {
7241
#if defined(HAVE_POSIX_ADVISE)
7242
  int res, posix_advice;
7243
  const char *advice_str;
7244

7245
  /* Convert from our advice values to the ones from the header; the
7246
   * indirection is needed for platforms which do not provide posix_fadvise(3).
7247
   */
7248
  switch (advice) {
7249
    case PR_FS_FADVISE_NORMAL:
7250
      advice_str = "NORMAL";
7251
      posix_advice = POSIX_FADV_NORMAL;
7252
      break;
7253

7254
    case PR_FS_FADVISE_RANDOM:
7255
      advice_str = "RANDOM";
7256
      posix_advice = POSIX_FADV_RANDOM;
7257
      break;
7258

7259
    case PR_FS_FADVISE_SEQUENTIAL:
7260
      advice_str = "SEQUENTIAL";
7261
      posix_advice = POSIX_FADV_SEQUENTIAL;
7262
      break;
7263

7264
    case PR_FS_FADVISE_WILLNEED:
7265
      advice_str = "WILLNEED";
7266
      posix_advice = POSIX_FADV_WILLNEED;
7267
      break;
7268

7269
    case PR_FS_FADVISE_DONTNEED:
7270
      advice_str = "DONTNEED";
7271
      posix_advice = POSIX_FADV_DONTNEED;
7272
      break;
7273

7274
    case PR_FS_FADVISE_NOREUSE:
7275
      advice_str = "NOREUSE";
7276
      posix_advice = POSIX_FADV_NOREUSE;
7277
      break;
7278

7279
    default:
7280
      pr_trace_msg(trace_channel, 9,
7281
        "unknown/unsupported advice: %d", advice);
7282
      return;
7283
  }
7284

7285
  res = posix_fadvise(fd, offset, len, posix_advice);
7286
  if (res < 0) {
7287
    pr_trace_msg(trace_channel, 9,
7288
      "posix_fadvise() error on fd %d (off %" PR_LU ", len %" PR_LU ", "
7289
      "advice %s): %s", fd, (pr_off_t) offset, (pr_off_t) len, advice_str,
7290
      strerror(errno));
7291
  }
12✔
7292
#endif
7293
}
44✔
7294

7295
int pr_fs_have_access(struct stat *st, int mode, uid_t uid, gid_t gid,
44✔
7296
    array_header *suppl_gids) {
7297
  mode_t mask;
44✔
7298

1✔
7299
  if (st == NULL) {
1✔
7300
    errno = EINVAL;
7301
    return -1;
7302
  }
7303

43✔
7304
  /* Root always succeeds for reads/writes. */
43✔
7305
  if (uid == PR_ROOT_UID &&
7306
      mode != X_OK) {
7307
    return 0;
7308
  }
7309

7310
  /* Initialize mask to reflect the permission bits that are applicable for
7311
   * the given user. mask contains the user-bits if the user ID equals the
7312
   * ID of the file owner. mask contains the group bits if the group ID
7313
   * belongs to the group of the file. mask will always contain the other
7314
   * bits of the permission bits.
29✔
7315
   */
7316
  mask = S_IROTH|S_IWOTH|S_IXOTH;
29✔
7317

11✔
7318
  if (st->st_uid == uid) {
7319
    mask |= S_IRUSR|S_IWUSR|S_IXUSR;
7320
  }
7321

7322
  /* Check the current group, as well as all supplementary groups.
7323
   * Fortunately, we have this information cached, so accessing it is
7324
   * almost free.
29✔
7325
   */
11✔
7326
  if (st->st_gid == gid) {
7327
    mask |= S_IRGRP|S_IWGRP|S_IXGRP;
7328

18✔
7329
  } else {
7330
    if (suppl_gids != NULL) {
7331
      register unsigned int i = 0;
12✔
7332

12✔
7333
      for (i = 0; i < suppl_gids->nelts; i++) {
6✔
7334
        if (st->st_gid == ((gid_t *) suppl_gids->elts)[i]) {
6✔
7335
          mask |= S_IRGRP|S_IWGRP|S_IXGRP;
7336
          break;
7337
        }
7338
      }
7339
    }
7340
  }
29✔
7341

7342
  mask &= st->st_mode;
7343

29✔
7344
  /* Perform requested access checks. */
8✔
7345
  if (mode & R_OK) {
4✔
7346
    if (!(mask & (S_IRUSR|S_IRGRP|S_IROTH))) {
4✔
7347
      errno = EACCES;
7348
      return -1;
7349
    }
7350
  }
25✔
7351

8✔
7352
  if (mode & W_OK) {
4✔
7353
    if (!(mask & (S_IWUSR|S_IWGRP|S_IWOTH))) {
4✔
7354
      errno = EACCES;
7355
      return -1;
7356
    }
7357
  }
21✔
7358

13✔
7359
  if (mode & X_OK) {
5✔
7360
    if (!(mask & (S_IXUSR|S_IXGRP|S_IXOTH))) {
5✔
7361
      errno = EACCES;
7362
      return -1;
7363
    }
7364
  }
7365

7366
  /* F_OK already checked by checking the return value of stat. */
7367
  return 0;
7368
}
2✔
7369

7370
int pr_fs_is_nfs(const char *path) {
2✔
7371
#if defined(HAVE_STATFS_F_TYPE) || defined(HAVE_STATFS_F_FSTYPENAME)
2✔
7372
  struct statfs fs;
7373
  int res = FALSE;
2✔
7374

1✔
7375
  if (path == NULL) {
1✔
7376
    errno = EINVAL;
7377
    return -1;
7378
  }
1✔
7379

1✔
7380
  pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
×
7381
  if (statfs(path, &fs) < 0) {
7382
    int xerrno = errno;
×
7383

7384
    pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7385
      path, strerror(xerrno));
×
7386

×
7387
    errno = xerrno;
7388
    return -1;
7389
  }
7390

7391
# if defined(HAVE_STATFS_F_FSTYPENAME)
7392
  pr_trace_msg(trace_channel, 12,
7393
    "path '%s' resides on a filesystem of type '%s'", path, fs.f_fstypename);
7394
  if (strcasecmp(fs.f_fstypename, "nfs") == 0) {
7395
    res = TRUE;
7396
  }
7397
# elif defined(HAVE_STATFS_F_TYPE)
1✔
7398
  /* Probably a Linux system. */
×
7399
  if (fs.f_type == NFS_SUPER_MAGIC) {
7400
    pr_trace_msg(trace_channel, 12,
7401
      "path '%s' resides on an NFS_SUPER_MAGIC filesystem (type 0x%08x)", path,
×
7402
      (int) fs.f_type);
7403
    res = TRUE;
7404

1✔
7405
  } else {
7406
    pr_trace_msg(trace_channel, 12,
7407
      "path '%s' resides on a filesystem of type 0x%08x (not NFS_SUPER_MAGIC)",
7408
      path, (int) fs.f_type);
7409
  }
7410
# endif
7411

7412
  return res;
7413

7414
#else
7415
  errno = ENOSYS;
7416
  return -1;
7417
#endif /* No HAVE_STATFS_F_FSTYPENAME or HAVE_STATFS_F_TYPE */
7418
}
6✔
7419

6✔
7420
int pr_fsio_puts(const char *buf, pr_fh_t *fh) {
6✔
7421
  if (fh == NULL ||
3✔
7422
      buf == NULL) {
3✔
7423
    errno = EINVAL;
7424
    return -1;
7425
  }
3✔
7426

7427
  return pr_fsio_write(fh, buf, strlen(buf));
7428
}
11✔
7429

11✔
7430
int pr_fsio_set_block(pr_fh_t *fh) {
7431
  int flags, res;
11✔
7432

1✔
7433
  if (fh == NULL) {
1✔
7434
    errno = EINVAL;
7435
    return -1;
7436
  }
10✔
7437

10✔
7438
  flags = fcntl(fh->fh_fd, F_GETFL);
7439
  if (flags < 0) {
7440
    return -1;
7441
  }
9✔
7442

9✔
7443
  res = fcntl(fh->fh_fd, F_SETFL, flags & (U32BITS ^ O_NONBLOCK));
7444
  return res;
7445
}
4✔
7446

4✔
7447
void pr_resolve_fs_map(void) {
7448
  register unsigned int i = 0;
4✔
7449

7450
  if (fs_map == NULL) {
7451
    return;
7452
  }
6✔
7453

3✔
7454
  for (i = 0; i < fs_map->nelts; i++) {
3✔
7455
    char *newpath = NULL;
3✔
7456
    int add_slash = FALSE;
7457
    pr_fs_t *fsi;
3✔
7458

3✔
7459
    pr_signals_handle();
7460
    fsi = ((pr_fs_t **) fs_map->elts)[i];
7461

3✔
7462
    /* Skip if this fs is the root fs. */
1✔
7463
    if (fsi == root_fs) {
7464
      continue;
7465
    }
7466

7467
    /* Note that dir_realpath() does _not_ handle "../blah" paths
7468
     * well, so...at least for now, hope that such paths are screened
7469
     * by the code adding such paths into the fs_map.  Check for
7470
     * a trailing slash in the unadjusted path, so that I know if I need
7471
     * to re-add that slash to the adjusted path -- these trailing slashes
7472
     * are important!
2✔
7473
     */
2✔
7474
    if ((strcmp(fsi->fs_path, "/") != 0 &&
1✔
7475
        (fsi->fs_path)[strlen(fsi->fs_path) - 1] == '/')) {
7476
      add_slash = TRUE;
7477
    }
2✔
7478

2✔
7479
    newpath = dir_realpath(fsi->fs_pool, fsi->fs_path);
7480
    if (newpath != NULL) {
×
7481

×
7482
      if (add_slash == TRUE) {
7483
        newpath = pstrcat(fsi->fs_pool, newpath, "/", NULL);
7484
      }
7485

7486
      /* Note that this does cause a slightly larger memory allocation from
7487
       * the pr_fs_t's pool, as the original path value was also allocated
7488
       * from that pool, and that original pointer is being overwritten.
7489
       * However, as this function is only called once, and that pool
7490
       * is freed later, I think this may be acceptable.
×
7491
       */
7492
      fsi->fs_path = newpath;
7493
    }
7494
  }
7495

3✔
7496
  /* Resort the map */
7497
  qsort(fs_map->elts, fs_map->nelts, sizeof(pr_fs_t *), fs_cmp);
7498
}
258✔
7499

258✔
7500
int init_fs(void) {
7501
  char cwdbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
7502

258✔
7503
  /* Establish the default pr_fs_t that will handle any path */
258✔
7504
  root_fs = pr_create_fs(permanent_pool, "system");
7505
  if (root_fs == NULL) {
7506

7507
    /* Do not insert this fs into the FS map.  This will allow other
7508
     * modules to insert filesystems at "/", if they want.
×
7509
     */
×
7510
    pr_log_pri(PR_LOG_WARNING, "error: unable to initialize default FS");
7511
    exit(1);
7512
  }
258✔
7513

7514
  root_fs->fs_path = pstrdup(root_fs->fs_pool, "/");
7515

258✔
7516
  /* Set the root FSIO handlers. */
258✔
7517
  root_fs->stat = sys_stat;
258✔
7518
  root_fs->fstat = sys_fstat;
258✔
7519
  root_fs->lstat = sys_lstat;
258✔
7520
  root_fs->rename = sys_rename;
258✔
7521
  root_fs->unlink = sys_unlink;
258✔
7522
  root_fs->open = sys_open;
258✔
7523
  root_fs->close = sys_close;
258✔
7524
  root_fs->pread = sys_pread;
258✔
7525
  root_fs->read = sys_read;
258✔
7526
  root_fs->pwrite = sys_pwrite;
258✔
7527
  root_fs->write = sys_write;
258✔
7528
  root_fs->lseek = sys_lseek;
258✔
7529
  root_fs->link = sys_link;
258✔
7530
  root_fs->readlink = sys_readlink;
258✔
7531
  root_fs->symlink = sys_symlink;
258✔
7532
  root_fs->ftruncate = sys_ftruncate;
258✔
7533
  root_fs->truncate = sys_truncate;
258✔
7534
  root_fs->chmod = sys_chmod;
258✔
7535
  root_fs->fchmod = sys_fchmod;
258✔
7536
  root_fs->chown = sys_chown;
258✔
7537
  root_fs->fchown = sys_fchown;
258✔
7538
  root_fs->lchown = sys_lchown;
258✔
7539
  root_fs->access = sys_access;
258✔
7540
  root_fs->faccess = sys_faccess;
258✔
7541
  root_fs->utimes = sys_utimes;
258✔
7542
  root_fs->futimes = sys_futimes;
258✔
7543
  root_fs->fsync = sys_fsync;
7544
  root_fs->realpath = sys_realpath;
258✔
7545

258✔
7546
  root_fs->getxattr = sys_getxattr;
258✔
7547
  root_fs->lgetxattr = sys_lgetxattr;
258✔
7548
  root_fs->fgetxattr = sys_fgetxattr;
258✔
7549
  root_fs->listxattr = sys_listxattr;
258✔
7550
  root_fs->llistxattr = sys_llistxattr;
258✔
7551
  root_fs->flistxattr = sys_flistxattr;
258✔
7552
  root_fs->removexattr = sys_removexattr;
258✔
7553
  root_fs->lremovexattr = sys_lremovexattr;
258✔
7554
  root_fs->fremovexattr = sys_fremovexattr;
258✔
7555
  root_fs->setxattr = sys_setxattr;
258✔
7556
  root_fs->lsetxattr = sys_lsetxattr;
7557
  root_fs->fsetxattr = sys_fsetxattr;
258✔
7558

258✔
7559
  root_fs->chdir = sys_chdir;
258✔
7560
  root_fs->chroot = sys_chroot;
258✔
7561
  root_fs->opendir = sys_opendir;
258✔
7562
  root_fs->closedir = sys_closedir;
258✔
7563
  root_fs->readdir = sys_readdir;
258✔
7564
  root_fs->mkdir = sys_mkdir;
7565
  root_fs->rmdir = sys_rmdir;
258✔
7566

258✔
7567
  if (getcwd(cwdbuf, sizeof(cwdbuf)-1)) {
258✔
7568
    cwdbuf[sizeof(cwdbuf)-1] = '\0';
7569
    pr_fs_setcwd(cwdbuf);
7570

×
7571
  } else {
×
7572
    pr_fsio_chdir("/", FALSE);
7573
    pr_fs_setcwd("/");
7574
  }
7575

258✔
7576
  /* Prepare the stat cache as well. */
258✔
7577
  statcache_pool = make_sub_pool(permanent_pool);
258✔
7578
  pr_pool_tag(statcache_pool, "FS Statcache Pool");
258✔
7579
  stat_statcache_tab = pr_table_alloc(statcache_pool, 0);
258✔
7580
  stat_statcache_set = xaset_create(statcache_pool, NULL);
258✔
7581
  lstat_statcache_tab = pr_table_alloc(statcache_pool, 0);
7582
  lstat_statcache_set = xaset_create(statcache_pool, NULL);
258✔
7583

7584
  return 0;
7585
}
7586

7587
#ifdef PR_USE_DEVEL
2✔
7588

2✔
7589
static const char *get_fs_hooks_str(pool *p, pr_fs_t *fs) {
7590
  char *hooks = "";
2✔
7591

2✔
7592
  if (fs->stat) {
7593
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "stat(2)", NULL);
7594
  }
2✔
7595

2✔
7596
  if (fs->lstat) {
7597
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lstat(2)", NULL);
7598
  }
2✔
7599

2✔
7600
  if (fs->fstat) {
7601
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fstat(2)", NULL);
7602
  }
2✔
7603

2✔
7604
  if (fs->rename) {
7605
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "rename(2)", NULL);
7606
  }
2✔
7607

2✔
7608
  if (fs->link) {
7609
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "link(2)", NULL);
7610
  }
2✔
7611

2✔
7612
  if (fs->unlink) {
7613
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "unlink(2)", NULL);
7614
  }
2✔
7615

2✔
7616
  if (fs->open) {
7617
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "open(2)", NULL);
7618
  }
2✔
7619

2✔
7620
  if (fs->close) {
7621
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "close(2)", NULL);
7622
  }
2✔
7623

2✔
7624
  if (fs->read) {
7625
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "read(2)", NULL);
7626
  }
2✔
7627

2✔
7628
  if (fs->lseek) {
7629
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lseek(2)", NULL);
7630
  }
2✔
7631

2✔
7632
  if (fs->readlink) {
7633
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "readlink(2)", NULL);
7634
  }
2✔
7635

2✔
7636
  if (fs->symlink) {
7637
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "symlink(2)", NULL);
7638
  }
2✔
7639

2✔
7640
  if (fs->ftruncate) {
7641
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "ftruncate(2)", NULL);
7642
  }
2✔
7643

2✔
7644
  if (fs->truncate) {
7645
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "truncate(2)", NULL);
7646
  }
2✔
7647

2✔
7648
  if (fs->chmod) {
7649
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chmod(2)", NULL);
7650
  }
2✔
7651

2✔
7652
  if (fs->chown) {
7653
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chown(2)", NULL);
7654
  }
2✔
7655

2✔
7656
  if (fs->fchown) {
7657
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fchown(2)", NULL);
7658
  }
2✔
7659

2✔
7660
  if (fs->lchown) {
7661
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lchown(2)", NULL);
7662
  }
2✔
7663

2✔
7664
  if (fs->access) {
7665
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "access(2)", NULL);
7666
  }
2✔
7667

2✔
7668
  if (fs->faccess) {
7669
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "faccess(2)", NULL);
7670
  }
2✔
7671

2✔
7672
  if (fs->utimes) {
7673
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "utimes(2)", NULL);
7674
  }
2✔
7675

2✔
7676
  if (fs->futimes) {
7677
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "futimes(2)", NULL);
7678
  }
2✔
7679

2✔
7680
  if (fs->fsync) {
7681
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fsync(2)", NULL);
7682
  }
2✔
7683

2✔
7684
  if (fs->chdir) {
7685
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chdir(2)", NULL);
7686
  }
2✔
7687

2✔
7688
  if (fs->chroot) {
7689
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chroot(2)", NULL);
7690
  }
2✔
7691

2✔
7692
  if (fs->opendir) {
7693
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "opendir(3)", NULL);
7694
  }
2✔
7695

2✔
7696
  if (fs->closedir) {
7697
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "closedir(3)", NULL);
7698
  }
2✔
7699

2✔
7700
  if (fs->readdir) {
7701
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "readdir(3)", NULL);
7702
  }
2✔
7703

2✔
7704
  if (fs->mkdir) {
7705
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "mkdir(2)", NULL);
7706
  }
2✔
7707

×
7708
  if (!*hooks) {
7709
    return pstrdup(p, "(none)");
7710
  }
7711

7712
  return hooks;
7713
}
2✔
7714

7715
static void get_fs_info(pool *p, int depth, pr_fs_t *fs,
7716
    void (*dumpf)(const char *, ...)) {
2✔
7717

7718
  dumpf("FS#%u: '%s', mounted at '%s', implementing the following hooks:",
2✔
7719
    depth, fs->fs_name, fs->fs_path);
2✔
7720
  dumpf("FS#%u:    %s", depth, get_fs_hooks_str(p, fs));
7721
}
10✔
7722

10✔
7723
static void fs_printf(const char *fmt, ...) {
10✔
7724
  char buf[PR_TUNABLE_BUFFER_SIZE+1];
7725
  va_list msg;
10✔
7726

10✔
7727
  memset(buf, '\0', sizeof(buf));
10✔
7728
  va_start(msg, fmt);
10✔
7729
  pr_vsnprintf(buf, sizeof(buf)-1, fmt, msg);
7730
  va_end(msg);
10✔
7731

10✔
7732
  buf[sizeof(buf)-1] = '\0';
10✔
7733
  pr_trace_msg(trace_channel, 19, "%s", buf);
7734
}
3✔
7735

3✔
7736
void pr_fs_dump(void (*dumpf)(const char *, ...)) {
7737
  pool *p;
3✔
7738

3✔
7739
  if (dumpf == NULL) {
7740
    dumpf = fs_printf;
7741
  }
3✔
7742

3✔
7743
  dumpf("FS#0: 'system' mounted at '/', implementing the following hooks:");
7744
  dumpf("FS#0:    (all)");
3✔
7745

2✔
7746
  if (!fs_map ||
7747
      fs_map->nelts == 0) {
7748
    return;
7749
  }
2✔
7750

7751
  p = make_sub_pool(permanent_pool);
2✔
7752

2✔
7753
  if (fs_map->nelts > 0) {
2✔
7754
    pr_fs_t **fs_objs = (pr_fs_t **) fs_map->elts;
7755
    register unsigned int i;
4✔
7756

2✔
7757
    for (i = 0; i < fs_map->nelts; i++) {
7758
      pr_fs_t *fsi = fs_objs[i];
4✔
7759

2✔
7760
      for (; fsi->fs_next; fsi = fsi->fs_next) {
7761
        get_fs_info(p, i+1, fsi, dumpf);
7762
      }
7763
    }
7764
  }
2✔
7765

7766
  destroy_pool(p);
7767
}
7768
#endif /* PR_USE_DEVEL */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc