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

proftpd / proftpd / 30135626854

25 Jul 2026 12:15AM UTC coverage: 93.034% (+0.6%) from 92.428%
30135626854

push

github

51363 of 55209 relevant lines covered (93.03%)

219.82 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-2026 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, see <https://www.gnu.org/licenses/>.
19
 *
20
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
21
 * and other respective copyright holders give permission to link this program
22
 * with OpenSSL, and distribute the resulting executable, without including
23
 * the source code for OpenSSL in the source distribution.
24
 */
25

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

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

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

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

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

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

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

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

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

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

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

72
typedef struct fsopendir fsopendir_t;
73

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

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

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

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

87
static fsopendir_t *fsopendir_list;
88

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

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

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

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

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

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

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

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

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

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

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

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

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

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

159
    errno = EACCES;
160
  }
15✔
161

162
  return res;
163
}
164

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

2✔
169
  (void) nwritten;
170

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

478
  return res;
479
}
3✔
480

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

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

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

1✔
494
  (void) p;
495

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

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

520
  return res;
521
}
1✔
522

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

1✔
527
  (void) p;
528

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

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

559
  return res;
560
}
1✔
561

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

1✔
566
  (void) p;
567

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

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

592
  return res;
593
}
1✔
594

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

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

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

611
    pr_signals_handle();
612

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

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

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

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

×
629
    pr_signals_handle();
630

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

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

641
  return names;
642
}
×
643

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

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

660
  return res;
661
}
3✔
662

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

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

685
  return res;
686
}
1✔
687

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

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

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

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

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

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

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

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

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

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

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

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

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

769
  res = (*names)->nelts;
770

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

781
  return (int) res;
782
}
×
783

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

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

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

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

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

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

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

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

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

835
  res = (*names)->nelts;
836

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

847
  return (int) res;
848
}
×
849

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

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

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

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

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

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

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

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

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

900
  res = (*names)->nelts;
901

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

912
  return (int) res;
913
}
×
914

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

1✔
919
  (void) p;
920

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

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

943
  return res;
944
}
1✔
945

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

1✔
950
  (void) p;
951

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

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

980
  return res;
981
}
1✔
982

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

1✔
986
  (void) p;
987

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

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

1010
  return res;
1011
}
1✔
1012

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

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

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

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

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

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

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

2✔
1046
  (void) p;
1047

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

1053
# elif defined(HAVE_SYS_XATTR_H)
1054
  xattr_flags = get_setxattr_flags(flags);
1055

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

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

1078
  return res;
1079
}
2✔
1080

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

2✔
1085
  (void) p;
1086

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

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

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

1123
  return res;
1124
}
2✔
1125

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

1✔
1130
  (void) p;
1131

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

1137
# elif defined(HAVE_SYS_XATTR_H)
1138
  xattr_flags = get_setxattr_flags(flags);
1139

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

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

1162
  return res;
1163
}
1✔
1164

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1231
    return 1;
1232
  }
×
1233

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1446
    pr_fs_clean_path2(pathbuf, cleaned_path, sizeof(cleaned_path)-1, 0);
1447

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

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

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

1464
  path_len = strlen(cleaned_path);
1465

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

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

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

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

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

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

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

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

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

1508
  return retval;
1509
}
1510

1511
/* Lookup routines */
1512

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

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

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

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

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

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

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

1564
  return fs;
1565
}
1566

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

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

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

1590
    mystat = fs->stat;
1591

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1680
/* FS Statcache API */
1681

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

330✔
1760
  return 0;
1761
}
330✔
1762

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

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

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

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

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

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

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

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

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

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

1799
    pr_fs_clean_path2(pathbuf, cleaned_path, sizeof(cleaned_path)-1, 0);
1800

68✔
1801
    res = 0;
1802

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

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

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

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

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

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

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

1839
  return res;
1840
}
1841

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

4✔
1846
/* FS functions proper */
1847

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

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

1865
  copy_iter_count = 0;
1866

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

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

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

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

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

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

1✔
1895
    pr_fsio_close(src_fh);
1896

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1976
  if (pr_fsio_fstat(dst_fh, &dst_st) == 0) {
1977

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

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

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

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

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

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

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

2✔
2016
    pr_signals_handle();
2017

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

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

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

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

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

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

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

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

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

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

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

2070
  free(buf);
2071

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

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

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

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

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

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

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

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

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

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

2128
#  endif /* !HAVE_PERM_COPY_FD */
2129

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

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

2138
    } else {
2139
      aclent_t *acls;
2140

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

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

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

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

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

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

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

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

2201
  (void) pr_fsio_close(src_fh);
2202

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

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

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

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

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

2227
    errno = xerrno;
2228
  }
×
2229

2230
  return res;
2231
}
2232

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

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

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

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

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

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

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

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

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

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

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

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

2325
  return fs;
2326
}
2327

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

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

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

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

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

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

278✔
2359
  return fs;
2360
}
278✔
2361

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

15✔
2453
  return TRUE;
2454
}
15✔
2455

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

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

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

2472
  fs_objs = (pr_fs_t **) fs_map->elts;
2473

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

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

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

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

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

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

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

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

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

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

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

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

×
2526
        return fsi;
2527
      }
×
2528

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

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

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

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

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

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

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

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

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

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

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

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

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

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

9✔
2612
    fs = fs_objs[i];
2613

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

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

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

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

2639
      chk_fs_map = FALSE;
2640

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

2646
  chk_fs_map = FALSE;
2647

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

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

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

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

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

266✔
2669
  return 0;
2670
}
266✔
2671

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

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

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

36✔
2685
  /* The shortest possible path is "/", which requires 2 bytes. */
2686

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

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

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

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

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

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

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

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

×
2739
    free(_dir1);
2740

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

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

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

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

2764
  sstrcat(ptr, _dir2, buflen);
2765

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

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

25✔
2774
  return 0;
2775
}
25✔
2776

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

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

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

2801
  memset(user, '\0', sizeof(user));
2802

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2917
  currlen = strlen(buf);
2918

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

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

2929
  return 1;
2930
}
2931

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3034
      sstrncpy(namebuf, workpath, sizeof(namebuf));
3035

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

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

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

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

92✔
3050
      where = ++ptr;
3051

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

9✔
3123
      sstrncpy(workpath, namebuf, sizeof(workpath));
3124
    }
3125
  }
3126

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

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

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

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

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

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

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

561✔
3164
  while (fini--) {
3165
    where = curpath;
3166

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

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

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

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

×
3192
        *last = '\0';
×
3193
        continue;
3194
      }
3195

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
3298
      sstrncpy(workpath, namebuf, sizeof(workpath));
3299
    }
3300
  }
3301

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
3363
          ptr++;
3364
        }
3365

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

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

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

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

3✔
3384
        *last = '\0';
3✔
3385
        continue;
3386
      }
3387

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

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

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

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

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

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

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

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

5,061✔
3423
      sstrncpy(workpath, namebuf, sizeof(workpath));
3424
    }
3425
  }
3426

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
3507
    return (char *) path;
3508
  }
3509

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

3519
  return (char *) path;
3520
#endif /* PR_USE_NLS */
3521
}
3522

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

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

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

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

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

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

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

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

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

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

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

×
3573
    return (char *) path;
3574
  }
3575

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

3585
  return (char *) path;
3586
#endif /* PR_USE_NLS */
3587
}
3588

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

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

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

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

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

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

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

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

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

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

3✔
3640
    return components;
3641
  }
3642

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

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

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

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

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

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

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

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

3679
  return components;
3680
}
3681

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

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

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

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

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

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

3710
  return path;
3711
}
3712

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

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

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

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

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

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

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

7✔
3753
  if (path == NULL) {
1✔
3754
    return;
3755
  }
3756

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

10✔
3848
      where = ++ptr;
3849

10✔
3850
      sstrncpy(workpath, namebuf, sizeof(workpath));
3851
    }
3852
  }
3853

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

6✔
3858
  sstrncpy(buf, workpath, buflen);
3859
}
3860

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

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

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

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

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

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

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

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

3903
  return res;
3904
}
3905

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

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

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

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

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

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

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

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

3945
  return res;
3946
}
3947

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

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

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

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

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

12✔
3974
    fs = lookup_dir_fs(buf, FSIO_DIR_OPENDIR);
3975
  }
3976

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

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

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

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

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

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

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

12✔
4017
  if (fsopendir_list) {
4018

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

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

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

4035
  return res;
4036
}
4037

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

79✔
4041
  if (fsopendir_list != NULL) {
4042
    fsopendir_t *fsod;
4043

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

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

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

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

12✔
4065
      destroy_pool(fsod->pool);
4066
    }
4067
  }
4068

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

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

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

79✔
4082
  return fs;
4083
}
4084

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

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

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

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

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

12✔
4109
  return res;
4110
}
4111

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

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

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

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

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

65✔
4136
  return res;
4137
}
4138

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

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

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

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

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

8✔
4170
  if (umask_ptr != NULL) {
×
4171
    dir_umask = *umask_ptr;
4172

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

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

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

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

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

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

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

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

3✔
4212
    errno = xerrno;
4213
  }
4214

3✔
4215
  return res;
4216
}
4217

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

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

149✔
4224
  return prev;
4225
}
4226

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

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

24✔
4233
  return prev;
4234
}
4235

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

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

2✔
4245
  prev_value = fsio_use_mkdtemp;
4246

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

2✔
4251
  return prev_value;
4252
}
4253

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

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

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

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

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

×
4298
    (void) close(fd);
4299

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

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

×
4310
    (void) close(fd);
4311

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

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

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

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

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

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

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

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

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

1✔
4364
  if (use_root) {
×
4365
    PRIVS_RELINQUISH
4366
  }
4367

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

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

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

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

4407
  return 0;
4408
}
4409

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

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

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

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

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

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

1✔
4448
    if (uid != (uid_t) -1) {
4449
      use_root_chown = TRUE;
4450

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

×
4454
      use_root_chown = TRUE;
4455

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

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

×
4467
    if (use_root_chown == FALSE) {
4468
      use_mkdtemp = FALSE;
4469
    }
4470
  }
4471

4472
#ifdef HAVE_MKDTEMP
2✔
4473
  if (use_mkdtemp == TRUE) {
1✔
4474
    char *ptr;
1✔
4475
    struct stat st;
4476

1✔
4477
    ptr = strrchr(path, '/');
1✔
4478
    if (ptr == NULL) {
×
4479
      errno = EINVAL;
×
4480
      return -1;
4481
    }
4482

1✔
4483
    if (ptr != path) {
1✔
4484
      dst_dirlen = (ptr - path);
1✔
4485
      dst_dir = pstrndup(p, path, dst_dirlen);
4486

4487
    } else {
4488
      dst_dirlen = 1;
4489
      dst_dir = "/";
4490
    }
4491

1✔
4492
    res = lstat(dst_dir, &st);
1✔
4493
    if (res < 0) {
×
4494
      xerrno = errno;
4495

×
4496
      pr_log_pri(PR_LOG_WARNING,
4497
        "smkdir: unable to lstat(2) parent directory '%s': %s", dst_dir,
4498
        strerror(xerrno));
×
4499
      pr_trace_msg(trace_channel, 1,
4500
        "smkdir: unable to lstat(2) parent directory '%s': %s", dst_dir,
4501
        strerror(xerrno));
4502

×
4503
      errno = xerrno;
×
4504
      return -1;
4505
    }
4506

1✔
4507
    if (!S_ISDIR(st.st_mode) &&
4508
        !S_ISLNK(st.st_mode)) {
×
4509
      errno = EPERM;
×
4510
      return -1;
4511
    }
4512

1✔
4513
    if (st.st_mode & S_ISGID) {
×
4514
      set_sgid = TRUE;
4515
    }
4516

4517
    /* Allocate enough space for the temporary name: the length of the
4518
     * destination directory, a slash, 9 X's, 3 for the prefix, and 1 for the
4519
     * trailing NUL.
4520
     */
1✔
4521
    tmpl_len = dst_dirlen + 15;
1✔
4522
    tmpl = pcalloc(p, tmpl_len);
1✔
4523
    pr_snprintf(tmpl, tmpl_len-1, "%s/.dstXXXXXXXXX",
4524
      dst_dirlen > 1 ? dst_dir : "");
4525

4526
    /* Use mkdtemp(3) to create the temporary directory (in the same destination
4527
     * directory as the target path).
4528
     */
1✔
4529
    tmpl_path = mkdtemp(tmpl);
1✔
4530
    if (tmpl_path == NULL) {
×
4531
      xerrno = errno;
4532

×
4533
      pr_log_pri(PR_LOG_WARNING,
4534
        "smkdir: mkdtemp(3) failed to create directory using '%s': %s", tmpl,
4535
        strerror(xerrno));
×
4536
      pr_trace_msg(trace_channel, 1,
4537
        "smkdir: mkdtemp(3) failed to create directory using '%s': %s", tmpl,
4538
        strerror(xerrno));
4539

×
4540
      errno = xerrno;
×
4541
      return -1;
4542
    }
4543

4544
  } else {
1✔
4545
    res = pr_fsio_mkdir(path, mode);
1✔
4546
    if (res < 0) {
×
4547
      xerrno = errno;
4548

×
4549
      pr_trace_msg(trace_channel, 1,
4550
        "mkdir(2) failed to create directory '%s' with perms %04o: %s", path,
4551
        mode, strerror(xerrno));
4552

×
4553
      errno = xerrno;
×
4554
      return -1;
4555
    }
4556

1✔
4557
    tmpl_path = pstrdup(p, path);
4558
  }
4559
#else
4560

4561
  res = pr_fsio_mkdir(path, mode);
4562
  if (res < 0) {
4563
    xerrno = errno;
4564

4565
    pr_trace_msg(trace_channel, 1,
4566
      "mkdir(2) failed to create directory '%s' with perms %04o: %s", path,
4567
      mode, strerror(xerrno));
4568

4569
    errno = xerrno;
4570
    return -1;
4571
  }
4572

4573
  tmpl_path = pstrdup(p, path);
4574
#endif /* HAVE_MKDTEMP */
4575

2✔
4576
  if (use_mkdtemp == TRUE) {
1✔
4577
    mode_t mask, *dir_umask, perms;
4578

4579
    /* mkdtemp(3) creates a directory with 0700 perms; we are given the
4580
     * target mode (modulo the configured Umask).
4581
     */
1✔
4582
    dir_umask = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
1✔
4583
    if (dir_umask == NULL) {
4584
      /* If Umask was configured with a single parameter, then DirUmask
4585
       * would not be present; we still should check for Umask.
4586
       */
1✔
4587
      dir_umask = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
4588
    }
4589

1✔
4590
    if (dir_umask) {
×
4591
      mask = *dir_umask;
4592

4593
    } else {
4594
      mask = (mode_t) 0022;
4595
    }
4596

1✔
4597
    perms = (mode & ~mask);
4598

1✔
4599
    if (set_sgid) {
×
4600
      perms |= S_ISGID;
4601
    }
4602

4603
    /* If we're setting the SGID bit, we need to use root privs, in order
4604
     * to reliably set the SGID bit.  Sigh.
4605
     */
1✔
4606
    res = schmod_dir(p, tmpl_path, perms, set_sgid);
1✔
4607
    xerrno = errno;
4608

1✔
4609
    if (set_sgid) {
×
4610
      if (res < 0 &&
×
4611
          xerrno == EPERM) {
4612
        /* Try again, this time without root privs.  NFS situations which
4613
         * squash root privs could cause the above chmod(2) to fail; it
4614
         * might succeed now that we've dropped root privs (Bug#3962).
4615
         */
×
4616
        res = schmod_dir(p, tmpl_path, perms, FALSE);
×
4617
        xerrno = errno;
4618
      }
4619
    }
4620

1✔
4621
    if (res < 0) {
×
4622
      pr_log_pri(PR_LOG_WARNING, "chmod(%s) failed: %s", tmpl_path,
4623
        strerror(xerrno));
4624

×
4625
      (void) rmdir(tmpl_path);
4626

×
4627
      errno = xerrno;
×
4628
      return -1;
4629
    }
4630
  }
4631

2✔
4632
  if (uid != (uid_t) -1) {
2✔
4633
    if (use_root_chown) {
1✔
4634
      PRIVS_ROOT
4635
    }
4636

2✔
4637
    res = pr_fsio_lchown(tmpl_path, uid, gid);
2✔
4638
    xerrno = errno;
4639

2✔
4640
    if (use_root_chown) {
1✔
4641
      PRIVS_RELINQUISH
4642
    }
4643

2✔
4644
    if (res < 0) {
×
4645
      pr_log_pri(PR_LOG_WARNING, "lchown(%s) as root failed: %s", tmpl_path,
4646
        strerror(xerrno));
4647

4648
    } else {
2✔
4649
      if (gid != (gid_t) -1) {
2✔
4650
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s, GID %s successful",
4651
          tmpl_path, pr_uid2str(p, uid), pr_gid2str(p, gid));
4652

4653
      } else {
×
4654
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s successful",
4655
          tmpl_path, pr_uid2str(NULL, uid));
4656
      }
4657
    }
4658

×
4659
  } else if (gid != (gid_t) -1) {
×
4660
    if (use_root_chown) {
×
4661
      PRIVS_ROOT
4662
    }
4663

×
4664
    res = pr_fsio_lchown(tmpl_path, (uid_t) -1, gid);
×
4665
    xerrno = errno;
4666

×
4667
    if (use_root_chown) {
×
4668
      PRIVS_RELINQUISH
4669
    }
4670

×
4671
    if (res < 0) {
×
4672
      pr_log_pri(PR_LOG_WARNING, "%slchown(%s) failed: %s",
4673
        use_root_chown ? "root " : "", tmpl_path, strerror(xerrno));
4674

4675
    } else {
×
4676
      pr_log_debug(DEBUG2, "%slchown(%s) to GID %s successful",
4677
        use_root_chown ? "root " : "", tmpl_path, pr_gid2str(p, gid));
4678
    }
4679
  }
4680

2✔
4681
  if (use_mkdtemp == TRUE) {
4682
    /* Use rename(2) to move the temporary directory into place at the
4683
     * target path.
4684
     */
1✔
4685
    res = rename(tmpl_path, path);
1✔
4686
    if (res < 0) {
×
4687
      xerrno = errno;
4688

×
4689
      pr_log_pri(PR_LOG_INFO, "renaming '%s' to '%s' failed: %s", tmpl_path,
4690
        path, strerror(xerrno));
4691

×
4692
      (void) rmdir(tmpl_path);
4693

4694
#ifdef ENOTEMPTY
×
4695
      if (xerrno == ENOTEMPTY) {
4696
        /* If the rename(2) failed with "Directory not empty" (ENOTEMPTY),
4697
         * then change the errno to "File exists" (EEXIST), so that the
4698
         * error reported to the client is more indicative of the actual
4699
         * cause.
4700
         */
×
4701
        xerrno = EEXIST;
4702
      }
4703
#endif /* ENOTEMPTY */
4704

×
4705
      errno = xerrno;
×
4706
      return -1;
4707
    }
4708
  }
4709

2✔
4710
  pr_fs_clear_cache2(path);
2✔
4711
  return 0;
4712
}
4713

11✔
4714
int pr_fsio_rmdir(const char *path) {
11✔
4715
  int res;
11✔
4716
  pr_fs_t *fs;
4717

11✔
4718
  if (path == NULL) {
1✔
4719
    errno = EINVAL;
1✔
4720
    return -1;
4721
  }
4722

10✔
4723
  fs = lookup_dir_fs(path, FSIO_DIR_RMDIR);
10✔
4724
  if (fs == NULL) {
4725
    return -1;
4726
  }
4727

4728
  /* Find the first non-NULL custom rmdir handler.  If there are none,
4729
   * use the system rmdir.
4730
   */
10✔
4731
  while (fs && fs->fs_next && !fs->rmdir) {
4732
    fs = fs->fs_next;
4733
  }
4734

10✔
4735
  pr_trace_msg(trace_channel, 8, "using %s rmdir() for path '%s'", fs->fs_name,
4736
    path);
10✔
4737
  res = (fs->rmdir)(fs, path);
10✔
4738
  if (res == 0) {
4✔
4739
    pr_fs_clear_cache2(path);
4740
  }
4741

4742
  return res;
4743
}
4744

2✔
4745
int pr_fsio_rmdir_with_error(pool *p, const char *path, pr_error_t **err) {
2✔
4746
  int res;
4747

2✔
4748
  res = pr_fsio_rmdir(path);
2✔
4749
  if (res < 0) {
2✔
4750
    int xerrno = errno;
4751

2✔
4752
    if (p != NULL &&
2✔
4753
        err != NULL) {
1✔
4754
      *err = pr_error_create(p, xerrno);
1✔
4755
      if (pr_error_explain_rmdir(*err, path) < 0) {
×
4756
        pr_error_destroy(*err);
×
4757
        *err = NULL;
4758
      }
4759
    }
4760

2✔
4761
    errno = xerrno;
4762
  }
4763

2✔
4764
  return res;
4765
}
4766

46✔
4767
int pr_fsio_stat(const char *path, struct stat *st) {
46✔
4768
  pr_fs_t *fs = NULL;
4769

46✔
4770
  if (path == NULL ||
46✔
4771
      st == NULL) {
2✔
4772
    errno = EINVAL;
2✔
4773
    return -1;
4774
  }
4775

44✔
4776
  fs = lookup_file_fs(path, NULL, FSIO_FILE_STAT);
44✔
4777
  if (fs == NULL) {
4778
    return -1;
4779
  }
4780

4781
  /* Find the first non-NULL custom stat handler.  If there are none,
4782
   * use the system stat.
4783
   */
44✔
4784
  while (fs && fs->fs_next && !fs->stat) {
4785
    fs = fs->fs_next;
4786
  }
4787

44✔
4788
  pr_trace_msg(trace_channel, 8, "using %s stat() for path '%s'", fs->fs_name,
4789
    path);
44✔
4790
  return fs_cache_stat(fs ? fs : root_fs, path, st);
4791
}
4792

3✔
4793
int pr_fsio_stat_with_error(pool *p, const char *path, struct stat *st,
4794
    pr_error_t **err) {
3✔
4795
  int res;
4796

3✔
4797
  res = pr_fsio_stat(path, st);
3✔
4798
  if (res < 0) {
3✔
4799
    int xerrno = errno;
4800

3✔
4801
    if (p != NULL &&
3✔
4802
        err != NULL) {
2✔
4803
      *err = pr_error_create(p, xerrno);
2✔
4804
      if (pr_error_explain_stat(*err, path, st) < 0) {
1✔
4805
        pr_error_destroy(*err);
1✔
4806
        *err = NULL;
4807
      }
4808
    }
4809

3✔
4810
    errno = xerrno;
4811
  }
4812

3✔
4813
  return res;
4814
}
4815

37✔
4816
int pr_fsio_fstat(pr_fh_t *fh, struct stat *st) {
37✔
4817
  int res;
37✔
4818
  pr_fs_t *fs;
4819

37✔
4820
  if (fh == NULL ||
37✔
4821
      st == NULL) {
1✔
4822
    errno = EINVAL;
1✔
4823
    return -1;
4824
  }
4825

4826
  /* Find the first non-NULL custom fstat handler.  If there are none,
4827
   * use the system fstat.
4828
   */
36✔
4829
  fs = fh->fh_fs;
36✔
4830
  while (fs && fs->fs_next && !fs->fstat) {
4831
    fs = fs->fs_next;
4832
  }
4833

36✔
4834
  pr_trace_msg(trace_channel, 8, "using %s fstat() for path '%s'", fs->fs_name,
4835
    fh->fh_path);
36✔
4836
  res = (fs->fstat)(fh, fh->fh_fd, st);
4837

36✔
4838
  return res;
4839
}
4840

34✔
4841
int pr_fsio_lstat(const char *path, struct stat *st) {
34✔
4842
  pr_fs_t *fs;
4843

34✔
4844
  if (path == NULL ||
34✔
4845
      st == NULL) {
2✔
4846
    errno = EINVAL;
2✔
4847
    return -1;
4848
  }
4849

32✔
4850
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LSTAT);
32✔
4851
  if (fs == NULL) {
4852
    return -1;
4853
  }
4854

4855
  /* Find the first non-NULL custom lstat handler.  If there are none,
4856
   * use the system lstat.
4857
   */
32✔
4858
  while (fs && fs->fs_next && !fs->lstat) {
4859
    fs = fs->fs_next;
4860
  }
4861

32✔
4862
  pr_trace_msg(trace_channel, 8, "using %s lstat() for path '%s'", fs->fs_name,
4863
    path);
32✔
4864
  return fs_cache_lstat(fs ? fs : root_fs, path, st);
4865
}
4866

3✔
4867
int pr_fsio_lstat_with_error(pool *p, const char *path, struct stat *st,
4868
    pr_error_t **err) {
3✔
4869
  int res;
4870

3✔
4871
  res = pr_fsio_lstat(path, st);
3✔
4872
  if (res < 0) {
3✔
4873
    int xerrno = errno;
4874

3✔
4875
    if (p != NULL &&
3✔
4876
        err != NULL) {
2✔
4877
      *err = pr_error_create(p, xerrno);
2✔
4878
      if (pr_error_explain_lstat(*err, path, st) < 0) {
1✔
4879
        pr_error_destroy(*err);
1✔
4880
        *err = NULL;
4881
      }
4882
    }
4883

3✔
4884
    errno = xerrno;
4885
  }
4886

3✔
4887
  return res;
4888
}
4889

29✔
4890
int pr_fsio_readlink(const char *path, char *buf, size_t buflen) {
29✔
4891
  int res;
29✔
4892
  pr_fs_t *fs;
4893

29✔
4894
  if (path == NULL ||
29✔
4895
      buf == NULL) {
1✔
4896
    errno = EINVAL;
1✔
4897
    return -1;
4898
  }
4899

28✔
4900
  fs = lookup_file_fs(path, NULL, FSIO_FILE_READLINK);
28✔
4901
  if (fs == NULL) {
4902
    return -1;
4903
  }
4904

4905
  /* Find the first non-NULL custom readlink handler.  If there are none,
4906
   * use the system readlink.
4907
   */
28✔
4908
  while (fs && fs->fs_next && !fs->readlink) {
4909
    fs = fs->fs_next;
4910
  }
4911

28✔
4912
  pr_trace_msg(trace_channel, 8, "using %s readlink() for path '%s'",
4913
    fs->fs_name, path);
28✔
4914
  res = (fs->readlink)(fs, path, buf, buflen);
4915

28✔
4916
  return res;
4917
}
4918

4919
/* pr_fs_glob() is just a wrapper for glob(3), setting the various gl_
4920
 * callbacks to our fs functions.
4921
 */
3✔
4922
int pr_fs_glob(const char *pattern, int flags,
4923
    int (*errfunc)(const char *, int), glob_t *pglob) {
4924

3✔
4925
  if (pattern == NULL ||
3✔
4926
      pglob == NULL) {
2✔
4927
    errno = EINVAL;
2✔
4928
    return -1;
4929
  }
4930

1✔
4931
  flags |= GLOB_ALTDIRFUNC;
4932

1✔
4933
  pglob->gl_closedir = (void (*)(void *)) pr_fsio_closedir;
1✔
4934
  pglob->gl_readdir = pr_fsio_readdir;
1✔
4935
  pglob->gl_opendir = pr_fsio_opendir;
1✔
4936
  pglob->gl_lstat = pr_fsio_lstat;
1✔
4937
  pglob->gl_stat = pr_fsio_stat;
4938

1✔
4939
  return glob(pattern, flags, errfunc, pglob);
4940
}
4941

2✔
4942
void pr_fs_globfree(glob_t *pglob) {
2✔
4943
  if (pglob != NULL) {
1✔
4944
    globfree(pglob);
4945
  }
2✔
4946
}
4947

10✔
4948
int pr_fsio_rename(const char *rnfr, const char *rnto) {
10✔
4949
  int res;
10✔
4950
  pr_fs_t *from_fs, *to_fs, *fs;
4951

10✔
4952
  if (rnfr == NULL ||
10✔
4953
      rnto == NULL) {
2✔
4954
    errno = EINVAL;
2✔
4955
    return -1;
4956
  }
4957

8✔
4958
  from_fs = lookup_file_fs(rnfr, NULL, FSIO_FILE_RENAME);
8✔
4959
  if (from_fs == NULL) {
4960
    return -1;
4961
  }
4962

8✔
4963
  to_fs = lookup_file_fs(rnto, NULL, FSIO_FILE_RENAME);
8✔
4964
  if (to_fs == NULL) {
4965
    return -1;
4966
  }
4967

8✔
4968
  if (from_fs->allow_xdev_rename == FALSE ||
8✔
4969
      to_fs->allow_xdev_rename == FALSE) {
×
4970
    if (from_fs != to_fs) {
×
4971
      errno = EXDEV;
×
4972
      return -1;
4973
    }
4974
  }
4975

4976
  fs = to_fs;
4977

4978
  /* Find the first non-NULL custom rename handler.  If there are none,
4979
   * use the system rename.
4980
   */
8✔
4981
  while (fs && fs->fs_next && !fs->rename) {
4982
    fs = fs->fs_next;
4983
  }
4984

8✔
4985
  pr_trace_msg(trace_channel, 8, "using %s rename() for paths '%s', '%s'",
4986
    fs->fs_name, rnfr, rnto);
8✔
4987
  res = (fs->rename)(fs, rnfr, rnto);
8✔
4988
  if (res == 0) {
1✔
4989
    pr_fs_clear_cache2(rnfr);
1✔
4990
    pr_fs_clear_cache2(rnto);
4991
  }
4992

4993
  return res;
4994
}
4995

3✔
4996
int pr_fsio_rename_with_error(pool *p, const char *rnfr, const char *rnto,
4997
    pr_error_t **err) {
3✔
4998
  int res;
4999

3✔
5000
  res = pr_fsio_rename(rnfr, rnto);
3✔
5001
  if (res < 0) {
3✔
5002
    int xerrno = errno;
5003

3✔
5004
    if (p != NULL &&
3✔
5005
        err != NULL) {
2✔
5006
      *err = pr_error_create(p, xerrno);
2✔
5007
      if (pr_error_explain_rename(*err, rnfr, rnto) < 0) {
1✔
5008
        pr_error_destroy(*err);
1✔
5009
        *err = NULL;
5010
      }
5011
    }
5012

3✔
5013
    errno = xerrno;
5014
  }
5015

3✔
5016
  return res;
5017
}
5018

76✔
5019
int pr_fsio_unlink(const char *name) {
76✔
5020
  int res;
76✔
5021
  pr_fs_t *fs;
5022

76✔
5023
  if (name == NULL) {
1✔
5024
    errno = EINVAL;
1✔
5025
    return -1;
5026
  }
5027

75✔
5028
  fs = lookup_file_fs(name, NULL, FSIO_FILE_UNLINK);
75✔
5029
  if (fs == NULL) {
5030
    return -1;
5031
  }
5032

5033
  /* Find the first non-NULL custom unlink handler.  If there are none,
5034
   * use the system unlink.
5035
   */
75✔
5036
  while (fs && fs->fs_next && !fs->unlink) {
5037
    fs = fs->fs_next;
5038
  }
5039

75✔
5040
  pr_trace_msg(trace_channel, 8, "using %s unlink() for path '%s'",
5041
    fs->fs_name, name);
75✔
5042
  res = (fs->unlink)(fs, name);
75✔
5043
  if (res == 0) {
28✔
5044
    pr_fs_clear_cache2(name);
5045
  }
5046

5047
  return res;
5048
}
5049

3✔
5050
int pr_fsio_unlink_with_error(pool *p, const char *path, pr_error_t **err) {
3✔
5051
  int res;
5052

3✔
5053
  res = pr_fsio_unlink(path);
3✔
5054
  if (res < 0) {
3✔
5055
    int xerrno = errno;
5056

3✔
5057
    if (p != NULL &&
3✔
5058
        err != NULL) {
2✔
5059
      *err = pr_error_create(p, xerrno);
2✔
5060
      if (pr_error_explain_unlink(*err, path) < 0) {
1✔
5061
        pr_error_destroy(*err);
1✔
5062
        *err = NULL;
5063
      }
5064
    }
5065

3✔
5066
    errno = xerrno;
5067
  }
5068

3✔
5069
  return res;
5070
}
5071

12✔
5072
pr_fh_t *pr_fsio_open_canon(const char *name, int flags) {
12✔
5073
  char *deref = NULL;
12✔
5074
  pool *tmp_pool = NULL;
12✔
5075
  pr_fh_t *fh = NULL;
12✔
5076
  pr_fs_t *fs = NULL;
5077

12✔
5078
  if (name == NULL) {
1✔
5079
    errno = EINVAL;
1✔
5080
    return NULL;
5081
  }
5082

11✔
5083
  fs = lookup_file_canon_fs(name, &deref, FSIO_FILE_OPEN);
11✔
5084
  if (fs == NULL) {
5085
    return NULL;
5086
  }
5087

5088
  /* Allocate a filehandle. */
11✔
5089
  tmp_pool = make_sub_pool(fs->fs_pool);
11✔
5090
  pr_pool_tag(tmp_pool, "pr_fsio_open_canon() subpool");
5091

11✔
5092
  fh = pcalloc(tmp_pool, sizeof(pr_fh_t));
11✔
5093
  fh->fh_pool = tmp_pool;
11✔
5094
  fh->fh_path = pstrdup(fh->fh_pool, name);
11✔
5095
  fh->fh_fd = -1;
11✔
5096
  fh->fh_buf = NULL;
11✔
5097
  fh->fh_fs = fs;
5098

5099
  /* Find the first non-NULL custom open handler.  If there are none,
5100
   * use the system open.
5101
   */
11✔
5102
  while (fs && fs->fs_next && !fs->open) {
5103
    fs = fs->fs_next;
5104
  }
5105

11✔
5106
  pr_trace_msg(trace_channel, 8, "using %s open() for path '%s'", fs->fs_name,
5107
    name);
11✔
5108
  fh->fh_fd = (fs->open)(fh, deref, flags);
11✔
5109
  if (fh->fh_fd < 0) {
2✔
5110
    int xerrno = errno;
5111

2✔
5112
    destroy_pool(fh->fh_pool);
5113

2✔
5114
    errno = xerrno;
2✔
5115
    return NULL;
5116
  }
5117

9✔
5118
  if ((flags & O_CREAT) ||
5119
      (flags & O_TRUNC)) {
×
5120
    pr_fs_clear_cache2(name);
5121
  }
5122

9✔
5123
  if (fcntl(fh->fh_fd, F_SETFD, FD_CLOEXEC) < 0) {
×
5124
    if (errno != EBADF) {
×
5125
      pr_trace_msg(trace_channel, 1, "error setting CLOEXEC on file fd %d: %s",
5126
        fh->fh_fd, strerror(errno));
5127
    }
5128
  }
5129

5130
  return fh;
5131
}
5132

72✔
5133
pr_fh_t *pr_fsio_open(const char *name, int flags) {
72✔
5134
  pool *tmp_pool = NULL;
72✔
5135
  pr_fh_t *fh = NULL;
72✔
5136
  pr_fs_t *fs = NULL;
5137

72✔
5138
  if (name == NULL) {
1✔
5139
    errno = EINVAL;
1✔
5140
    return NULL;
5141
  }
5142

71✔
5143
  fs = lookup_file_fs(name, NULL, FSIO_FILE_OPEN);
71✔
5144
  if (fs == NULL) {
5145
    return NULL;
5146
  }
5147

5148
  /* Allocate a filehandle. */
71✔
5149
  tmp_pool = make_sub_pool(fs->fs_pool);
71✔
5150
  pr_pool_tag(tmp_pool, "pr_fsio_open() subpool");
5151

71✔
5152
  fh = pcalloc(tmp_pool, sizeof(pr_fh_t));
71✔
5153
  fh->fh_pool = tmp_pool;
71✔
5154
  fh->fh_path = pstrdup(fh->fh_pool, name);
71✔
5155
  fh->fh_fd = -1;
71✔
5156
  fh->fh_buf = NULL;
71✔
5157
  fh->fh_fs = fs;
5158

5159
  /* Find the first non-NULL custom open handler.  If there are none,
5160
   * use the system open.
5161
   */
71✔
5162
  while (fs && fs->fs_next && !fs->open) {
5163
    fs = fs->fs_next;
5164
  }
5165

71✔
5166
  pr_trace_msg(trace_channel, 8, "using %s open() for path '%s'", fs->fs_name,
5167
    name);
71✔
5168
  fh->fh_fd = (fs->open)(fh, name, flags);
71✔
5169
  if (fh->fh_fd < 0) {
11✔
5170
    int xerrno = errno;
5171

11✔
5172
    destroy_pool(fh->fh_pool);
5173

11✔
5174
    errno = xerrno;
11✔
5175
    return NULL;
5176
  }
5177

60✔
5178
  if ((flags & O_CREAT) ||
5179
      (flags & O_TRUNC)) {
39✔
5180
    pr_fs_clear_cache2(name);
5181
  }
5182

60✔
5183
  if (fcntl(fh->fh_fd, F_SETFD, FD_CLOEXEC) < 0) {
×
5184
    if (errno != EBADF) {
×
5185
      pr_trace_msg(trace_channel, 1, "error setting CLOEXEC on file fd %d: %s",
5186
        fh->fh_fd, strerror(errno));
5187
    }
5188
  }
5189

5190
  return fh;
5191
}
5192

3✔
5193
pr_fh_t *pr_fsio_open_with_error(pool *p, const char *name, int flags,
5194
    pr_error_t **err) {
3✔
5195
  pr_fh_t *fh;
5196

3✔
5197
  fh = pr_fsio_open(name, flags);
3✔
5198
  if (fh == NULL) {
3✔
5199
    int xerrno = errno;
5200

3✔
5201
    if (p != NULL &&
3✔
5202
        err != NULL) {
2✔
5203
      *err = pr_error_create(p, xerrno);
2✔
5204
      if (pr_error_explain_open(*err, name, flags, PR_OPEN_MODE) < 0) {
1✔
5205
        pr_error_destroy(*err);
1✔
5206
        *err = NULL;
5207
      }
5208
    }
5209

3✔
5210
    errno = xerrno;
5211
  }
5212

3✔
5213
  return fh;
5214
}
5215

71✔
5216
int pr_fsio_close(pr_fh_t *fh) {
71✔
5217
  int res = 0, xerrno = 0;
71✔
5218
  pr_fs_t *fs;
5219

71✔
5220
  if (fh == NULL) {
4✔
5221
    errno = EINVAL;
4✔
5222
    return -1;
5223
  }
5224

5225
  /* Find the first non-NULL custom close handler.  If there are none,
5226
   * use the system close.
5227
   */
67✔
5228
  fs = fh->fh_fs;
67✔
5229
  while (fs && fs->fs_next && !fs->close) {
5230
    fs = fs->fs_next;
5231
  }
5232

67✔
5233
  pr_trace_msg(trace_channel, 8, "using %s close() for path '%s'", fs->fs_name,
5234
    fh->fh_path);
67✔
5235
  res = (fs->close)(fh, fh->fh_fd);
67✔
5236
  xerrno = errno;
5237

67✔
5238
  if (res == 0) {
67✔
5239
    pr_fs_clear_cache2(fh->fh_path);
5240
  }
5241

5242
  /* Make sure to scrub any buffered memory, too. */
67✔
5243
  if (fh->fh_buf != NULL) {
15✔
5244
    pr_buffer_t *pbuf;
5245

15✔
5246
    pbuf = fh->fh_buf;
15✔
5247
    pr_memscrub(pbuf->buf, pbuf->buflen);
5248
  }
5249

67✔
5250
  if (fh->fh_pool != NULL) {
67✔
5251
    destroy_pool(fh->fh_pool);
5252
  }
5253

67✔
5254
  errno = xerrno;
67✔
5255
  return res;
5256
}
5257

3✔
5258
int pr_fsio_close_with_error(pool *p, pr_fh_t *fh, pr_error_t **err) {
3✔
5259
  int res;
5260

3✔
5261
  res = pr_fsio_close(fh);
3✔
5262
  if (res < 0) {
3✔
5263
    int xerrno = errno;
5264

3✔
5265
    if (p != NULL &&
3✔
5266
        err != NULL) {
2✔
5267
      int fd = -1;
5268

2✔
5269
      *err = pr_error_create(p, xerrno);
5270

2✔
5271
      if (fh != NULL) {
×
5272
        fd = fh->fh_fd;
5273
      }
5274

2✔
5275
      if (pr_error_explain_close(*err, fd) < 0) {
1✔
5276
        pr_error_destroy(*err);
1✔
5277
        *err = NULL;
5278
      }
5279
    }
5280

3✔
5281
    errno = xerrno;
5282
  }
5283

3✔
5284
  return res;
5285
}
5286

4✔
5287
ssize_t pr_fsio_pread(pr_fh_t *fh, void *buf, size_t size, off_t offset) {
4✔
5288
  ssize_t res;
4✔
5289
  pr_fs_t *fs;
5290

4✔
5291
  if (fh == NULL ||
4✔
5292
      buf == NULL ||
5293
      size == 0) {
3✔
5294
    errno = EINVAL;
3✔
5295
    return -1;
5296
  }
5297

5298
  /* Find the first non-NULL custom pread handler.  If there are none,
5299
   * use the system pread.
5300
   */
1✔
5301
  fs = fh->fh_fs;
1✔
5302
  while (fs && fs->fs_next && !fs->pread) {
5303
    fs = fs->fs_next;
5304
  }
5305

1✔
5306
  pr_trace_msg(trace_channel, 8, "using %s pread() for path '%s' (%lu bytes, %"
5307
    PR_LU " offset)", fs->fs_name, fh->fh_path, (unsigned long) size,
5308
    (pr_off_t) offset);
1✔
5309
  res = (fs->pread)(fh, fh->fh_fd, buf, size, offset);
5310

1✔
5311
  return res;
5312
}
5313

41✔
5314
int pr_fsio_read(pr_fh_t *fh, char *buf, size_t size) {
41✔
5315
  int res;
41✔
5316
  pr_fs_t *fs;
5317

41✔
5318
  if (fh == NULL ||
41✔
5319
      buf == NULL ||
5320
      size == 0) {
6✔
5321
    errno = EINVAL;
6✔
5322
    return -1;
5323
  }
5324

5325
  /* Find the first non-NULL custom read handler.  If there are none,
5326
   * use the system read.
5327
   */
35✔
5328
  fs = fh->fh_fs;
35✔
5329
  while (fs && fs->fs_next && !fs->read) {
5330
    fs = fs->fs_next;
5331
  }
5332

35✔
5333
  pr_trace_msg(trace_channel, 8, "using %s read() for path '%s' (%lu bytes)",
5334
    fs->fs_name, fh->fh_path, (unsigned long) size);
35✔
5335
  res = (fs->read)(fh, fh->fh_fd, buf, size);
5336

35✔
5337
  return res;
5338
}
5339

3✔
5340
int pr_fsio_read_with_error(pool *p, pr_fh_t *fh, char *buf, size_t sz,
5341
    pr_error_t **err) {
3✔
5342
  int res;
5343

3✔
5344
  res = pr_fsio_read(fh, buf, sz);
3✔
5345
  if (res < 0) {
3✔
5346
    int xerrno = errno;
5347

3✔
5348
    if (p != NULL &&
3✔
5349
        err != NULL) {
2✔
5350
      int fd = -1;
5351

2✔
5352
      if (fh != NULL) {
×
5353
        fd = fh->fh_fd;
5354
      }
5355

2✔
5356
      *err = pr_error_create(p, xerrno);
2✔
5357
      if (pr_error_explain_read(*err, fd, buf, sz) < 0) {
1✔
5358
        pr_error_destroy(*err);
1✔
5359
        *err = NULL;
5360
      }
5361
    }
5362

3✔
5363
    errno = xerrno;
5364
  }
5365

3✔
5366
  return res;
5367
}
5368

4✔
5369
ssize_t pr_fsio_pwrite(pr_fh_t *fh, const void *buf, size_t size,
5370
    off_t offset) {
4✔
5371
  ssize_t res;
4✔
5372
  pr_fs_t *fs;
5373

4✔
5374
  if (fh == NULL ||
4✔
5375
      buf == NULL) {
2✔
5376
    errno = EINVAL;
2✔
5377
    return -1;
5378
  }
5379

5380
  /* Find the first non-NULL custom pwrite handler.  If there are none,
5381
   * use the system pwrite.
5382
   */
2✔
5383
  fs = fh->fh_fs;
2✔
5384
  while (fs && fs->fs_next && !fs->pwrite) {
5385
    fs = fs->fs_next;
5386
  }
5387

2✔
5388
  pr_trace_msg(trace_channel, 8, "using %s pwrite() for path '%s' (%lu bytes, %"
5389
    PR_LU " offset)", fs->fs_name, fh->fh_path, (unsigned long) size,
5390
    (pr_off_t) offset);
2✔
5391
  res = (fs->pwrite)(fh, fh->fh_fd, buf, size, offset);
5392

2✔
5393
  return res;
5394
}
5395

23✔
5396
int pr_fsio_write(pr_fh_t *fh, const char *buf, size_t size) {
23✔
5397
  int res;
23✔
5398
  pr_fs_t *fs;
5399

23✔
5400
  if (fh == NULL ||
23✔
5401
      buf == NULL) {
5✔
5402
    errno = EINVAL;
5✔
5403
    return -1;
5404
  }
5405

5406
  /* Find the first non-NULL custom write handler.  If there are none,
5407
   * use the system write.
5408
   */
18✔
5409
  fs = fh->fh_fs;
18✔
5410
  while (fs && fs->fs_next && !fs->write) {
5411
    fs = fs->fs_next;
5412
  }
5413

18✔
5414
  pr_trace_msg(trace_channel, 8, "using %s write() for path '%s' (%lu bytes)",
5415
    fs->fs_name, fh->fh_path, (unsigned long) size);
18✔
5416
  res = (fs->write)(fh, fh->fh_fd, buf, size);
5417

18✔
5418
  return res;
5419
}
5420

3✔
5421
int pr_fsio_write_with_error(pool *p, pr_fh_t *fh, const char *buf, size_t sz,
5422
    pr_error_t **err) {
3✔
5423
  int res;
5424

3✔
5425
  res = pr_fsio_write(fh, buf, sz);
3✔
5426
  if (res < 0) {
3✔
5427
    int xerrno = errno;
5428

3✔
5429
    if (p != NULL &&
3✔
5430
        err != NULL) {
2✔
5431
      int fd = -1;
5432

2✔
5433
      if (fh != NULL) {
×
5434
        fd = fh->fh_fd;
5435
      }
5436

2✔
5437
      *err = pr_error_create(p, xerrno);
2✔
5438
      if (pr_error_explain_write(*err, fd, buf, sz) < 0) {
1✔
5439
        pr_error_destroy(*err);
1✔
5440
        *err = NULL;
5441
      }
5442
    }
5443

3✔
5444
    errno = xerrno;
5445
  }
5446

3✔
5447
  return res;
5448
}
5449

4✔
5450
off_t pr_fsio_lseek(pr_fh_t *fh, off_t offset, int whence) {
4✔
5451
  off_t res;
4✔
5452
  pr_fs_t *fs;
5453

4✔
5454
  if (fh == NULL) {
1✔
5455
    errno = EINVAL;
1✔
5456
    return -1;
5457
  }
5458

5459
  /* Find the first non-NULL custom lseek handler.  If there are none,
5460
   * use the system lseek.
5461
   */
3✔
5462
  fs = fh->fh_fs;
3✔
5463
  while (fs && fs->fs_next && !fs->lseek) {
5464
    fs = fs->fs_next;
5465
  }
5466

3✔
5467
  pr_trace_msg(trace_channel, 8, "using %s lseek() for path '%s'", fs->fs_name,
5468
    fh->fh_path);
3✔
5469
  res = (fs->lseek)(fh, fh->fh_fd, offset, whence);
5470

3✔
5471
  return res;
5472
}
5473

7✔
5474
int pr_fsio_link(const char *target_path, const char *link_path) {
7✔
5475
  int res;
7✔
5476
  pr_fs_t *target_fs, *link_fs, *fs;
5477

7✔
5478
  if (target_path == NULL ||
7✔
5479
      link_path == NULL) {
3✔
5480
    errno = EINVAL;
3✔
5481
    return -1;
5482
  }
5483

4✔
5484
  target_fs = lookup_file_fs(target_path, NULL, FSIO_FILE_LINK);
4✔
5485
  if (target_fs == NULL) {
5486
    return -1;
5487
  }
5488

4✔
5489
  link_fs = lookup_file_fs(link_path, NULL, FSIO_FILE_LINK);
4✔
5490
  if (link_fs == NULL) {
5491
    return -1;
5492
  }
5493

4✔
5494
  if (target_fs->allow_xdev_link == FALSE ||
4✔
5495
      link_fs->allow_xdev_link == FALSE) {
×
5496
    if (target_fs != link_fs) {
×
5497
      errno = EXDEV;
×
5498
      return -1;
5499
    }
5500
  }
5501

5502
  fs = link_fs;
5503

5504
  /* Find the first non-NULL custom link handler.  If there are none,
5505
   * use the system link.
5506
   */
4✔
5507
  while (fs && fs->fs_next && !fs->link) {
5508
    fs = fs->fs_next;
5509
  }
5510

4✔
5511
  pr_trace_msg(trace_channel, 8, "using %s link() for paths '%s', '%s'",
5512
    fs->fs_name, target_path, link_path);
4✔
5513
  res = (fs->link)(fs, target_path, link_path);
4✔
5514
  if (res == 0) {
1✔
5515
    pr_fs_clear_cache2(link_path);
5516
  }
5517

5518
  return res;
5519
}
5520

12✔
5521
int pr_fsio_symlink(const char *target_path, const char *link_path) {
12✔
5522
  int res;
12✔
5523
  pr_fs_t *fs;
5524

12✔
5525
  if (target_path == NULL ||
12✔
5526
      link_path == NULL) {
3✔
5527
    errno = EINVAL;
3✔
5528
    return -1;
5529
  }
5530

9✔
5531
  fs = lookup_file_fs(link_path, NULL, FSIO_FILE_SYMLINK);
9✔
5532
  if (fs == NULL) {
5533
    return -1;
5534
  }
5535

5536
  /* Find the first non-NULL custom symlink handler.  If there are none,
5537
   * use the system symlink.
5538
   */
9✔
5539
  while (fs && fs->fs_next && !fs->symlink) {
5540
    fs = fs->fs_next;
5541
  }
5542

9✔
5543
  pr_trace_msg(trace_channel, 8, "using %s symlink() for path '%s'",
5544
    fs->fs_name, link_path);
9✔
5545
  res = (fs->symlink)(fs, target_path, link_path);
9✔
5546
  if (res == 0) {
6✔
5547
    pr_fs_clear_cache2(link_path);
5548
  }
5549

5550
  return res;
5551
}
5552

3✔
5553
int pr_fsio_ftruncate(pr_fh_t *fh, off_t len) {
3✔
5554
  int res;
3✔
5555
  pr_fs_t *fs;
5556

3✔
5557
  if (fh == NULL) {
1✔
5558
    errno = EINVAL;
1✔
5559
    return -1;
5560
  }
5561

5562
  /* Find the first non-NULL custom ftruncate handler.  If there are none,
5563
   * use the system ftruncate.
5564
   */
2✔
5565
  fs = fh->fh_fs;
2✔
5566
  while (fs && fs->fs_next && !fs->ftruncate) {
5567
    fs = fs->fs_next;
5568
  }
5569

2✔
5570
  pr_trace_msg(trace_channel, 8, "using %s ftruncate() for path '%s'",
5571
    fs->fs_name, fh->fh_path);
2✔
5572
  res = (fs->ftruncate)(fh, fh->fh_fd, len);
2✔
5573
  if (res == 0) {
2✔
5574
    pr_fs_clear_cache2(fh->fh_path);
5575

5576
    /* Clear any read buffer. */
2✔
5577
    if (fh->fh_buf != NULL) {
1✔
5578
      fh->fh_buf->current = fh->fh_buf->buf;
1✔
5579
      fh->fh_buf->remaining = fh->fh_buf->buflen;
5580
    }
5581
  }
5582

5583
  return res;
5584
}
5585

7✔
5586
int pr_fsio_truncate(const char *path, off_t len) {
7✔
5587
  int res;
7✔
5588
  pr_fs_t *fs;
5589

7✔
5590
  if (path == NULL) {
1✔
5591
    errno = EINVAL;
1✔
5592
    return -1;
5593
  }
5594

6✔
5595
  fs = lookup_file_fs(path, NULL, FSIO_FILE_TRUNC);
6✔
5596
  if (fs == NULL) {
5597
    return -1;
5598
  }
5599

5600
  /* Find the first non-NULL custom truncate handler.  If there are none,
5601
   * use the system truncate.
5602
   */
6✔
5603
  while (fs && fs->fs_next && !fs->truncate) {
5604
    fs = fs->fs_next;
5605
  }
5606

6✔
5607
  pr_trace_msg(trace_channel, 8, "using %s truncate() for path '%s'",
5608
    fs->fs_name, path);
6✔
5609
  res = (fs->truncate)(fs, path, len);
6✔
5610
  if (res == 0) {
3✔
5611
    pr_fs_clear_cache2(path);
5612
  }
5613

5614
  return res;
5615
}
5616

8✔
5617
int pr_fsio_chmod(const char *name, mode_t mode) {
8✔
5618
  int res;
8✔
5619
  pr_fs_t *fs;
5620

8✔
5621
  if (name == NULL) {
1✔
5622
    errno = EINVAL;
1✔
5623
    return -1;
5624
  }
5625

7✔
5626
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHMOD);
7✔
5627
  if (fs == NULL) {
5628
    return -1;
5629
  }
5630

5631
  /* Find the first non-NULL custom chmod handler.  If there are none,
5632
   * use the system chmod.
5633
   */
7✔
5634
  while (fs && fs->fs_next && !fs->chmod) {
5635
    fs = fs->fs_next;
5636
  }
5637

7✔
5638
  pr_trace_msg(trace_channel, 8, "using %s chmod() for path '%s'",
5639
    fs->fs_name, name);
7✔
5640
  res = (fs->chmod)(fs, name, mode);
7✔
5641
  if (res == 0) {
1✔
5642
    pr_fs_clear_cache2(name);
5643
  }
5644

5645
  return res;
5646
}
5647

3✔
5648
int pr_fsio_chmod_with_error(pool *p, const char *path, mode_t mode,
5649
    pr_error_t **err) {
3✔
5650
  int res;
5651

3✔
5652
  res = pr_fsio_chmod(path, mode);
3✔
5653
  if (res < 0) {
3✔
5654
    int xerrno = errno;
5655

3✔
5656
    if (p != NULL &&
3✔
5657
        err != NULL) {
2✔
5658
      *err = pr_error_create(p, xerrno);
2✔
5659
      if (pr_error_explain_chmod(*err, path, mode) < 0) {
1✔
5660
        pr_error_destroy(*err);
1✔
5661
        *err = NULL;
5662
      }
5663
    }
5664

3✔
5665
    errno = xerrno;
5666
  }
5667

3✔
5668
  return res;
5669
}
5670

5✔
5671
int pr_fsio_fchmod(pr_fh_t *fh, mode_t mode) {
5✔
5672
  int res;
5✔
5673
  pr_fs_t *fs;
5674

5✔
5675
  if (fh == NULL) {
4✔
5676
    errno = EINVAL;
4✔
5677
    return -1;
5678
  }
5679

5680
  /* Find the first non-NULL custom fchmod handler.  If there are none, use
5681
   * the system fchmod.
5682
   */
1✔
5683
  fs = fh->fh_fs;
1✔
5684
  while (fs && fs->fs_next && !fs->fchmod) {
5685
    fs = fs->fs_next;
5686
  }
5687

1✔
5688
  pr_trace_msg(trace_channel, 8, "using %s fchmod() for path '%s'",
5689
    fs->fs_name, fh->fh_path);
1✔
5690
  res = (fs->fchmod)(fh, fh->fh_fd, mode);
1✔
5691
  if (res == 0) {
1✔
5692
    pr_fs_clear_cache2(fh->fh_path);
5693
  }
5694

5695
  return res;
5696
}
5697

3✔
5698
int pr_fsio_fchmod_with_error(pool *p, pr_fh_t *fh, mode_t mode,
5699
    pr_error_t **err) {
3✔
5700
  int res;
5701

3✔
5702
  res = pr_fsio_fchmod(fh, mode);
3✔
5703
  if (res < 0) {
3✔
5704
    int xerrno = errno;
5705

3✔
5706
    if (p != NULL &&
3✔
5707
        err != NULL) {
2✔
5708
      int fd = -1;
5709

2✔
5710
      if (fh != NULL) {
×
5711
        fd = fh->fh_fd;
5712
      }
5713

2✔
5714
      *err = pr_error_create(p, xerrno);
2✔
5715
      if (pr_error_explain_fchmod(*err, fd, mode) < 0) {
1✔
5716
        pr_error_destroy(*err);
1✔
5717
        *err = NULL;
5718
      }
5719
    }
5720

3✔
5721
    errno = xerrno;
5722
  }
5723

3✔
5724
  return res;
5725
}
5726

8✔
5727
int pr_fsio_chown(const char *name, uid_t uid, gid_t gid) {
8✔
5728
  int res;
8✔
5729
  pr_fs_t *fs;
5730

8✔
5731
  if (name == NULL) {
1✔
5732
    errno = EINVAL;
1✔
5733
    return -1;
5734
  }
5735

7✔
5736
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHOWN);
7✔
5737
  if (fs == NULL) {
5738
    return -1;
5739
  }
5740

5741
  /* Find the first non-NULL custom chown handler.  If there are none,
5742
   * use the system chown.
5743
   */
7✔
5744
  while (fs && fs->fs_next && !fs->chown) {
5745
    fs = fs->fs_next;
5746
  }
5747

7✔
5748
  pr_trace_msg(trace_channel, 8, "using %s chown() for path '%s'",
5749
    fs->fs_name, name);
7✔
5750
  res = (fs->chown)(fs, name, uid, gid);
7✔
5751
  if (res == 0) {
1✔
5752
    pr_fs_clear_cache2(name);
5753
  }
5754

5755
  return res;
5756
}
5757

3✔
5758
int pr_fsio_chown_with_error(pool *p, const char *path, uid_t uid, gid_t gid,
5759
    pr_error_t **err) {
3✔
5760
  int res;
5761

3✔
5762
  res = pr_fsio_chown(path, uid, gid);
3✔
5763
  if (res < 0) {
3✔
5764
    int xerrno = errno;
5765

3✔
5766
    if (p != NULL &&
3✔
5767
        err != NULL) {
2✔
5768
      *err = pr_error_create(p, xerrno);
2✔
5769
      if (pr_error_explain_chown(*err, path, uid, gid) < 0) {
1✔
5770
        pr_error_destroy(*err);
1✔
5771
        *err = NULL;
5772
      }
5773
    }
5774

3✔
5775
    errno = xerrno;
5776
  }
5777

3✔
5778
  return res;
5779
}
5780

5✔
5781
int pr_fsio_fchown(pr_fh_t *fh, uid_t uid, gid_t gid) {
5✔
5782
  int res;
5✔
5783
  pr_fs_t *fs;
5784

5✔
5785
  if (fh == NULL) {
4✔
5786
    errno = EINVAL;
4✔
5787
    return -1;
5788
  }
5789

5790
  /* Find the first non-NULL custom fchown handler.  If there are none, use
5791
   * the system fchown.
5792
   */
1✔
5793
  fs = fh->fh_fs;
1✔
5794
  while (fs && fs->fs_next && !fs->fchown) {
5795
    fs = fs->fs_next;
5796
  }
5797

1✔
5798
  pr_trace_msg(trace_channel, 8, "using %s fchown() for path '%s'",
5799
    fs->fs_name, fh->fh_path);
1✔
5800
  res = (fs->fchown)(fh, fh->fh_fd, uid, gid);
1✔
5801
  if (res == 0) {
1✔
5802
    pr_fs_clear_cache2(fh->fh_path);
5803
  }
5804

5805
  return res;
5806
}
5807

3✔
5808
int pr_fsio_fchown_with_error(pool *p, pr_fh_t *fh, uid_t uid, gid_t gid,
5809
    pr_error_t **err) {
3✔
5810
  int res;
5811

3✔
5812
  res = pr_fsio_fchown(fh, uid, gid);
3✔
5813
  if (res < 0) {
3✔
5814
    int xerrno = errno;
5815

3✔
5816
    if (p != NULL &&
3✔
5817
        err != NULL) {
2✔
5818
      int fd = -1;
5819

2✔
5820
      if (fh != NULL) {
×
5821
        fd = fh->fh_fd;
5822
      }
5823

2✔
5824
      *err = pr_error_create(p, xerrno);
2✔
5825
      if (pr_error_explain_fchown(*err, fd, uid, gid) < 0) {
1✔
5826
        pr_error_destroy(*err);
1✔
5827
        *err = NULL;
5828
      }
5829
    }
5830

3✔
5831
    errno = xerrno;
5832
  }
5833

3✔
5834
  return res;
5835
}
5836

10✔
5837
int pr_fsio_lchown(const char *name, uid_t uid, gid_t gid) {
10✔
5838
  int res;
10✔
5839
  pr_fs_t *fs;
5840

10✔
5841
  if (name == NULL) {
1✔
5842
    errno = EINVAL;
1✔
5843
    return -1;
5844
  }
5845

9✔
5846
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHOWN);
9✔
5847
  if (fs == NULL) {
5848
    return -1;
5849
  }
5850

5851
  /* Find the first non-NULL custom lchown handler.  If there are none,
5852
   * use the system chown.
5853
   */
9✔
5854
  while (fs && fs->fs_next && !fs->lchown) {
5855
    fs = fs->fs_next;
5856
  }
5857

9✔
5858
  pr_trace_msg(trace_channel, 8, "using %s lchown() for path '%s'",
5859
    fs->fs_name, name);
9✔
5860
  res = (fs->lchown)(fs, name, uid, gid);
9✔
5861
  if (res == 0) {
3✔
5862
    pr_fs_clear_cache2(name);
5863
  }
5864

5865
  return res;
5866
}
5867

3✔
5868
int pr_fsio_lchown_with_error(pool *p, const char *path, uid_t uid, gid_t gid,
5869
    pr_error_t **err) {
3✔
5870
  int res;
5871

3✔
5872
  res = pr_fsio_lchown(path, uid, gid);
3✔
5873
  if (res < 0) {
3✔
5874
    int xerrno = errno;
5875

3✔
5876
    if (p != NULL &&
3✔
5877
        err != NULL) {
2✔
5878
      *err = pr_error_create(p, xerrno);
2✔
5879
      if (pr_error_explain_lchown(*err, path, uid, gid) < 0) {
1✔
5880
        pr_error_destroy(*err);
1✔
5881
        *err = NULL;
5882
      }
5883
    }
5884

3✔
5885
    errno = xerrno;
5886
  }
5887

3✔
5888
  return res;
5889
}
5890

16✔
5891
int pr_fsio_access(const char *path, int mode, uid_t uid, gid_t gid,
5892
    array_header *suppl_gids) {
16✔
5893
  pr_fs_t *fs;
5894

16✔
5895
  if (path == NULL) {
1✔
5896
    errno = EINVAL;
1✔
5897
    return -1;
5898
  }
5899

15✔
5900
  fs = lookup_file_fs(path, NULL, FSIO_FILE_ACCESS);
15✔
5901
  if (fs == NULL) {
5902
    return -1;
5903
  }
5904

5905
  /* Find the first non-NULL custom access handler.  If there are none,
5906
   * use the system access.
5907
   */
15✔
5908
  while (fs && fs->fs_next && !fs->access) {
5909
    fs = fs->fs_next;
5910
  }
5911

15✔
5912
  pr_trace_msg(trace_channel, 8, "using %s access() for path '%s'",
5913
    fs->fs_name, path);
15✔
5914
  return (fs->access)(fs, path, mode, uid, gid, suppl_gids);
5915
}
5916

5✔
5917
int pr_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
5918
    array_header *suppl_gids) {
5✔
5919
  pr_fs_t *fs;
5920

5✔
5921
  if (fh == NULL) {
1✔
5922
    errno = EINVAL;
1✔
5923
    return -1;
5924
  }
5925

5926
  /* Find the first non-NULL custom faccess handler.  If there are none,
5927
   * use the system faccess.
5928
   */
4✔
5929
  fs = fh->fh_fs;
4✔
5930
  while (fs && fs->fs_next && !fs->faccess) {
5931
    fs = fs->fs_next;
5932
  }
5933

4✔
5934
  pr_trace_msg(trace_channel, 8, "using %s faccess() for path '%s'",
5935
    fs->fs_name, fh->fh_path);
4✔
5936
  return (fs->faccess)(fh, mode, uid, gid, suppl_gids);
5937
}
5938

5✔
5939
int pr_fsio_utimes(const char *path, struct timeval *tvs) {
5✔
5940
  int res;
5✔
5941
  pr_fs_t *fs;
5942

5✔
5943
  if (path == NULL ||
5✔
5944
      tvs == NULL) {
1✔
5945
    errno = EINVAL;
1✔
5946
    return -1;
5947
  }
5948

4✔
5949
  fs = lookup_file_fs(path, NULL, FSIO_FILE_UTIMES);
4✔
5950
  if (fs == NULL) {
5951
    return -1;
5952
  }
5953

5954
  /* Find the first non-NULL custom utimes handler.  If there are none,
5955
   * use the system utimes.
5956
   */
4✔
5957
  while (fs && fs->fs_next && !fs->utimes) {
5958
    fs = fs->fs_next;
5959
  }
5960

4✔
5961
  pr_trace_msg(trace_channel, 8, "using %s utimes() for path '%s'",
5962
    fs->fs_name, path);
4✔
5963
  res = (fs->utimes)(fs, path, tvs);
4✔
5964
  if (res == 0) {
1✔
5965
    pr_fs_clear_cache2(path);
5966
  }
5967

5968
  return res;
5969
}
5970

5971
/* If the utimes(2) call fails because the process UID does not match the file
5972
 * UID, then check to see if the GIDs match (and that the file has group write
5973
 * permissions).
5974
 *
5975
 * This can be alleviated in two ways: a) if mod_cap is present, enable the
5976
 * CAP_FOWNER capability for the session, or b) use root privs.
5977
 */
×
5978
int pr_fsio_utimes_with_root(const char *path, struct timeval *tvs) {
×
5979
  int res, xerrno, matching_gid = FALSE;
×
5980
  struct stat st;
5981

×
5982
  res = pr_fsio_utimes(path, tvs);
×
5983
  xerrno = errno;
5984

×
5985
  if (res == 0) {
5986
    return 0;
5987
  }
5988

5989
  /* We only try these workarounds for EPERM. */
×
5990
  if (xerrno != EPERM) {
5991
    return res;
5992
  }
5993

×
5994
  pr_fs_clear_cache2(path);
×
5995
  if (pr_fsio_stat(path, &st) < 0) {
×
5996
    errno = xerrno;
×
5997
    return -1;
5998
  }
5999

6000
  /* Be sure to check the primary and all the supplemental groups to which
6001
   * this session belongs.
6002
   */
×
6003
  if (st.st_gid == session.gid) {
6004
    matching_gid = TRUE;
6005

×
6006
  } else if (session.gids != NULL) {
×
6007
    register unsigned int i;
×
6008
    gid_t *gids;
6009

×
6010
    gids = session.gids->elts;
×
6011
    for (i = 0; i < session.gids->nelts; i++) {
×
6012
      if (st.st_gid == gids[i]) {
6013
        matching_gid = TRUE;
6014
        break;
6015
      }
6016
    }
6017
  }
6018

×
6019
  if (matching_gid == TRUE &&
×
6020
      (st.st_mode & S_IWGRP)) {
6021

6022
    /* Try the utimes(2) call again, this time with root privs. */
×
6023
    pr_signals_block();
×
6024
    PRIVS_ROOT
×
6025
    res = pr_fsio_utimes(path, tvs);
×
6026
    PRIVS_RELINQUISH
×
6027
    pr_signals_unblock();
6028

×
6029
    if (res == 0) {
6030
      return 0;
6031
    }
6032
  }
6033

×
6034
  errno = xerrno;
×
6035
  return -1;
6036
}
6037

2✔
6038
int pr_fsio_futimes(pr_fh_t *fh, struct timeval *tvs) {
2✔
6039
  int res;
2✔
6040
  pr_fs_t *fs;
6041

2✔
6042
  if (fh == NULL ||
2✔
6043
      tvs == NULL) {
1✔
6044
    errno = EINVAL;
1✔
6045
    return -1;
6046
  }
6047

6048
  /* Find the first non-NULL custom futimes handler.  If there are none,
6049
   * use the system futimes.
6050
   */
1✔
6051
  fs = fh->fh_fs;
1✔
6052
  while (fs && fs->fs_next && !fs->futimes) {
6053
    fs = fs->fs_next;
6054
  }
6055

1✔
6056
  pr_trace_msg(trace_channel, 8, "using %s futimes() for path '%s'",
6057
    fs->fs_name, fh->fh_path);
1✔
6058
  res = (fs->futimes)(fh, fh->fh_fd, tvs);
1✔
6059
  if (res == 0) {
1✔
6060
    pr_fs_clear_cache2(fh->fh_path);
6061
  }
6062

6063
  return res;
6064
}
6065

4✔
6066
int pr_fsio_fsync(pr_fh_t *fh) {
4✔
6067
  int res;
4✔
6068
  pr_fs_t *fs;
6069

4✔
6070
  if (fh == NULL) {
1✔
6071
    errno = EINVAL;
1✔
6072
    return -1;
6073
  }
6074

6075
  /* Find the first non-NULL custom fsync handler.  If there are none,
6076
   * use the system fsync.
6077
   */
3✔
6078
  fs = fh->fh_fs;
3✔
6079
  while (fs && fs->fs_next && !fs->fsync) {
6080
    fs = fs->fs_next;
6081
  }
6082

3✔
6083
  pr_trace_msg(trace_channel, 8, "using %s fsync() for path '%s'",
6084
    fs->fs_name, fh->fh_path);
3✔
6085
  res = (fs->fsync)(fh, fh->fh_fd);
3✔
6086
  if (res == 0) {
3✔
6087
    pr_fs_clear_cache2(fh->fh_path);
6088
  }
6089

6090
  return res;
6091
}
6092

28✔
6093
const char *pr_fsio_realpath(pool *p, const char *path) {
28✔
6094
  const char *res;
28✔
6095
  pr_fs_t *fs;
6096

28✔
6097
  if (p == NULL ||
28✔
6098
      path == NULL) {
2✔
6099
    errno = EINVAL;
2✔
6100
    return NULL;
6101
  }
6102

26✔
6103
  fs = lookup_file_fs(path, NULL, FSIO_FILE_REALPATH);
26✔
6104
  if (fs == NULL) {
6105
    return NULL;
6106
  }
6107

6108
  /* Find the first non-NULL custom realpath handler.  If there are none,
6109
   * use the system realpath
6110
   */
26✔
6111
  while (fs && fs->fs_next && !fs->realpath) {
6112
    fs = fs->fs_next;
6113
  }
6114

26✔
6115
  pr_trace_msg(trace_channel, 8, "using %s realpath() for path '%s'",
6116
    fs->fs_name, path);
26✔
6117
  res = (fs->realpath)(fs, p, path);
6118

26✔
6119
  return res;
6120
}
6121

5✔
6122
ssize_t pr_fsio_getxattr(pool *p, const char *path, const char *name, void *val,
6123
    size_t valsz) {
5✔
6124
  ssize_t res;
5✔
6125
  pr_fs_t *fs;
6126

5✔
6127
  if (p == NULL ||
5✔
6128
      path == NULL ||
6129
      name == NULL) {
3✔
6130
    errno = EINVAL;
3✔
6131
    return -1;
6132
  }
6133

2✔
6134
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6135
    errno = ENOSYS;
1✔
6136
    return -1;
6137
  }
6138

1✔
6139
  fs = lookup_file_fs(path, NULL, FSIO_FILE_GETXATTR);
1✔
6140
  if (fs == NULL) {
6141
    return -1;
6142
  }
6143

6144
  /* Find the first non-NULL custom getxattr handler.  If there are none,
6145
   * use the system getxattr.
6146
   */
1✔
6147
  while (fs && fs->fs_next && !fs->getxattr) {
6148
    fs = fs->fs_next;
6149
  }
6150

1✔
6151
  pr_trace_msg(trace_channel, 8, "using %s getxattr() for path '%s'",
6152
    fs->fs_name, path);
1✔
6153
  res = (fs->getxattr)(p, fs, path, name, val, valsz);
1✔
6154
  return res;
6155
}
6156

5✔
6157
ssize_t pr_fsio_lgetxattr(pool *p, const char *path, const char *name,
6158
    void *val, size_t valsz) {
5✔
6159
  ssize_t res;
5✔
6160
  pr_fs_t *fs;
6161

5✔
6162
  if (p == NULL ||
5✔
6163
      path == NULL ||
6164
      name == NULL) {
3✔
6165
    errno = EINVAL;
3✔
6166
    return -1;
6167
  }
6168

2✔
6169
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6170
    errno = ENOSYS;
1✔
6171
    return -1;
6172
  }
6173

1✔
6174
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LGETXATTR);
1✔
6175
  if (fs == NULL) {
6176
    return -1;
6177
  }
6178

6179
  /* Find the first non-NULL custom lgetxattr handler.  If there are none,
6180
   * use the system lgetxattr.
6181
   */
1✔
6182
  while (fs && fs->fs_next && !fs->lgetxattr) {
6183
    fs = fs->fs_next;
6184
  }
6185

1✔
6186
  pr_trace_msg(trace_channel, 8, "using %s lgetxattr() for path '%s'",
6187
    fs->fs_name, path);
1✔
6188
  res = (fs->lgetxattr)(p, fs, path, name, val, valsz);
1✔
6189
  return res;
6190
}
6191

5✔
6192
ssize_t pr_fsio_fgetxattr(pool *p, pr_fh_t *fh, const char *name, void *val,
6193
    size_t valsz) {
5✔
6194
  ssize_t res;
5✔
6195
  pr_fs_t *fs;
6196

5✔
6197
  if (p == NULL ||
5✔
6198
      fh == NULL ||
6199
      name == NULL) {
3✔
6200
    errno = EINVAL;
3✔
6201
    return -1;
6202
  }
6203

2✔
6204
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6205
    errno = ENOSYS;
1✔
6206
    return -1;
6207
  }
6208

6209
  /* Find the first non-NULL custom fgetxattr handler.  If there are none,
6210
   * use the system fgetxattr.
6211
   */
1✔
6212
  fs = fh->fh_fs;
1✔
6213
  while (fs && fs->fs_next && !fs->fgetxattr) {
6214
    fs = fs->fs_next;
6215
  }
6216

1✔
6217
  pr_trace_msg(trace_channel, 8, "using %s fgetxattr() for path '%s'",
6218
    fs->fs_name, fh->fh_path);
1✔
6219
  res = (fs->fgetxattr)(p, fh, fh->fh_fd, name, val, valsz);
1✔
6220
  return res;
6221
}
6222

7✔
6223
int pr_fsio_listxattr(pool *p, const char *path, array_header **names) {
7✔
6224
  int res;
7✔
6225
  pr_fs_t *fs;
6226

7✔
6227
  if (p == NULL ||
7✔
6228
      path == NULL ||
6229
      names == NULL) {
3✔
6230
    errno = EINVAL;
3✔
6231
    return -1;
6232
  }
6233

4✔
6234
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6235
    errno = ENOSYS;
1✔
6236
    return -1;
6237
  }
6238

3✔
6239
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LISTXATTR);
3✔
6240
  if (fs == NULL) {
6241
    return -1;
6242
  }
6243

6244
  /* Find the first non-NULL custom listxattr handler.  If there are none,
6245
   * use the system listxattr.
6246
   */
3✔
6247
  while (fs && fs->fs_next && !fs->listxattr) {
6248
    fs = fs->fs_next;
6249
  }
6250

3✔
6251
  pr_trace_msg(trace_channel, 8, "using %s listxattr() for path '%s'",
6252
    fs->fs_name, path);
3✔
6253
  res = (fs->listxattr)(p, fs, path, names);
3✔
6254
  return res;
6255
}
6256

5✔
6257
int pr_fsio_llistxattr(pool *p, const char *path, array_header **names) {
5✔
6258
  int res;
5✔
6259
  pr_fs_t *fs;
6260

5✔
6261
  if (p == NULL ||
5✔
6262
      path == NULL ||
6263
      names == NULL) {
3✔
6264
    errno = EINVAL;
3✔
6265
    return -1;
6266
  }
6267

2✔
6268
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6269
    errno = ENOSYS;
1✔
6270
    return -1;
6271
  }
6272

1✔
6273
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LLISTXATTR);
1✔
6274
  if (fs == NULL) {
6275
    return -1;
6276
  }
6277

6278
  /* Find the first non-NULL custom llistxattr handler.  If there are none,
6279
   * use the system llistxattr.
6280
   */
1✔
6281
  while (fs && fs->fs_next && !fs->llistxattr) {
6282
    fs = fs->fs_next;
6283
  }
6284

1✔
6285
  pr_trace_msg(trace_channel, 8, "using %s llistxattr() for path '%s'",
6286
    fs->fs_name, path);
1✔
6287
  res = (fs->llistxattr)(p, fs, path, names);
1✔
6288
  return res;
6289
}
6290

7✔
6291
int pr_fsio_flistxattr(pool *p, pr_fh_t *fh, array_header **names) {
7✔
6292
  int res;
7✔
6293
  pr_fs_t *fs;
6294

7✔
6295
  if (p == NULL ||
7✔
6296
      fh == NULL ||
6297
      names == NULL) {
3✔
6298
    errno = EINVAL;
3✔
6299
    return -1;
6300
  }
6301

4✔
6302
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6303
    errno = ENOSYS;
1✔
6304
    return -1;
6305
  }
6306

6307
  /* Find the first non-NULL custom flistxattr handler.  If there are none,
6308
   * use the system flistxattr.
6309
   */
3✔
6310
  fs = fh->fh_fs;
3✔
6311
  while (fs && fs->fs_next && !fs->flistxattr) {
6312
    fs = fs->fs_next;
6313
  }
6314

3✔
6315
  pr_trace_msg(trace_channel, 8, "using %s flistxattr() for path '%s'",
6316
    fs->fs_name, fh->fh_path);
3✔
6317
  res = (fs->flistxattr)(p, fh, fh->fh_fd, names);
3✔
6318
  return res;
6319
}
6320

5✔
6321
int pr_fsio_removexattr(pool *p, const char *path, const char *name) {
5✔
6322
  int res;
5✔
6323
  pr_fs_t *fs;
6324

5✔
6325
  if (p == NULL ||
5✔
6326
      path == NULL ||
6327
      name == NULL) {
3✔
6328
    errno = EINVAL;
3✔
6329
    return -1;
6330
  }
6331

2✔
6332
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6333
    errno = ENOSYS;
1✔
6334
    return -1;
6335
  }
6336

1✔
6337
  fs = lookup_file_fs(path, NULL, FSIO_FILE_REMOVEXATTR);
1✔
6338
  if (fs == NULL) {
6339
    return -1;
6340
  }
6341

6342
  /* Find the first non-NULL custom removexattr handler.  If there are none,
6343
   * use the system removexattr.
6344
   */
1✔
6345
  while (fs && fs->fs_next && !fs->removexattr) {
6346
    fs = fs->fs_next;
6347
  }
6348

1✔
6349
  pr_trace_msg(trace_channel, 8, "using %s removexattr() for path '%s'",
6350
    fs->fs_name, path);
1✔
6351
  res = (fs->removexattr)(p, fs, path, name);
1✔
6352
  return res;
6353
}
6354

5✔
6355
int pr_fsio_lremovexattr(pool *p, const char *path, const char *name) {
5✔
6356
  int res;
5✔
6357
  pr_fs_t *fs;
6358

5✔
6359
  if (p == NULL ||
5✔
6360
      path == NULL ||
6361
      name == NULL) {
3✔
6362
    errno = EINVAL;
3✔
6363
    return -1;
6364
  }
6365

2✔
6366
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6367
    errno = ENOSYS;
1✔
6368
    return -1;
6369
  }
6370

1✔
6371
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LREMOVEXATTR);
1✔
6372
  if (fs == NULL) {
6373
    return -1;
6374
  }
6375

6376
  /* Find the first non-NULL custom lremovexattr handler.  If there are none,
6377
   * use the system lremovexattr.
6378
   */
1✔
6379
  while (fs && fs->fs_next && !fs->lremovexattr) {
6380
    fs = fs->fs_next;
6381
  }
6382

1✔
6383
  pr_trace_msg(trace_channel, 8, "using %s lremovexattr() for path '%s'",
6384
    fs->fs_name, path);
1✔
6385
  res = (fs->lremovexattr)(p, fs, path, name);
1✔
6386
  return res;
6387
}
6388

5✔
6389
int pr_fsio_fremovexattr(pool *p, pr_fh_t *fh, const char *name) {
5✔
6390
  int res;
5✔
6391
  pr_fs_t *fs;
6392

5✔
6393
  if (p == NULL ||
5✔
6394
      fh == NULL ||
6395
      name == NULL) {
3✔
6396
    errno = EINVAL;
3✔
6397
    return -1;
6398
  }
6399

2✔
6400
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6401
    errno = ENOSYS;
1✔
6402
    return -1;
6403
  }
6404

6405
  /* Find the first non-NULL custom fremovexattr handler.  If there are none,
6406
   * use the system fremovexattr.
6407
   */
1✔
6408
  fs = fh->fh_fs;
1✔
6409
  while (fs && fs->fs_next && !fs->fremovexattr) {
6410
    fs = fs->fs_next;
6411
  }
6412

1✔
6413
  pr_trace_msg(trace_channel, 8, "using %s fremovexattr() for path '%s'",
6414
    fs->fs_name, fh->fh_path);
1✔
6415
  res = (fs->fremovexattr)(p, fh, fh->fh_fd, name);
1✔
6416
  return res;
6417
}
6418

6✔
6419
int pr_fsio_setxattr(pool *p, const char *path, const char *name, void *val,
6420
    size_t valsz, int flags) {
6✔
6421
  int res;
6✔
6422
  pr_fs_t *fs;
6423

6✔
6424
  if (p == NULL ||
6✔
6425
      path == NULL ||
6426
      name == NULL) {
3✔
6427
    errno = EINVAL;
3✔
6428
    return -1;
6429
  }
6430

3✔
6431
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6432
    errno = ENOSYS;
1✔
6433
    return -1;
6434
  }
6435

2✔
6436
  fs = lookup_file_fs(path, NULL, FSIO_FILE_SETXATTR);
2✔
6437
  if (fs == NULL) {
6438
    return -1;
6439
  }
6440

6441
  /* Find the first non-NULL custom setxattr handler.  If there are none,
6442
   * use the system setxattr.
6443
   */
2✔
6444
  while (fs && fs->fs_next && !fs->setxattr) {
6445
    fs = fs->fs_next;
6446
  }
6447

2✔
6448
  pr_trace_msg(trace_channel, 8, "using %s setxattr() for path '%s'",
6449
    fs->fs_name, path);
2✔
6450
  res = (fs->setxattr)(p, fs, path, name, val, valsz, flags);
2✔
6451
  return res;
6452
}
6453

6✔
6454
int pr_fsio_lsetxattr(pool *p, const char *path, const char *name, void *val,
6455
    size_t valsz, int flags) {
6✔
6456
  int res;
6✔
6457
  pr_fs_t *fs;
6458

6✔
6459
  if (p == NULL ||
6✔
6460
      path == NULL ||
6461
      name == NULL) {
3✔
6462
    errno = EINVAL;
3✔
6463
    return -1;
6464
  }
6465

3✔
6466
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6467
    errno = ENOSYS;
1✔
6468
    return -1;
6469
  }
6470

2✔
6471
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LSETXATTR);
2✔
6472
  if (fs == NULL) {
6473
    return -1;
6474
  }
6475

6476
  /* Find the first non-NULL custom lsetxattr handler.  If there are none,
6477
   * use the system lsetxattr.
6478
   */
2✔
6479
  while (fs && fs->fs_next && !fs->lsetxattr) {
6480
    fs = fs->fs_next;
6481
  }
6482

2✔
6483
  pr_trace_msg(trace_channel, 8, "using %s lsetxattr() for path '%s'",
6484
    fs->fs_name, path);
2✔
6485
  res = (fs->lsetxattr)(p, fs, path, name, val, valsz, flags);
2✔
6486
  return res;
6487
}
6488

5✔
6489
int pr_fsio_fsetxattr(pool *p, pr_fh_t *fh, const char *name, void *val,
6490
    size_t valsz, int flags) {
5✔
6491
  int res;
5✔
6492
  pr_fs_t *fs;
6493

5✔
6494
  if (p == NULL ||
5✔
6495
      fh == NULL ||
6496
      name == NULL) {
3✔
6497
    errno = EINVAL;
3✔
6498
    return -1;
6499
  }
6500

2✔
6501
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
1✔
6502
    errno = ENOSYS;
1✔
6503
    return -1;
6504
  }
6505

6506
  /* Find the first non-NULL custom fsetxattr handler.  If there are none,
6507
   * use the system fsetxattr.
6508
   */
1✔
6509
  fs = fh->fh_fs;
1✔
6510
  while (fs && fs->fs_next && !fs->fsetxattr) {
6511
    fs = fs->fs_next;
6512
  }
6513

1✔
6514
  pr_trace_msg(trace_channel, 8, "using %s fsetxattr() for path '%s'",
6515
    fs->fs_name, fh->fh_path);
1✔
6516
  res = (fs->fsetxattr)(p, fh, fh->fh_fd, name, val, valsz, flags);
1✔
6517
  return res;
6518
}
6519

6520
/* If the wrapped chroot() function succeeds (e.g. returns 0), then all
6521
 * pr_fs_ts currently registered in the fs_map will have their paths
6522
 * rewritten to reflect the new root.
6523
 */
9✔
6524
int pr_fsio_chroot(const char *path) {
9✔
6525
  int res = 0, xerrno = 0;
9✔
6526
  pr_fs_t *fs;
6527

9✔
6528
  if (path == NULL) {
1✔
6529
    errno = EINVAL;
1✔
6530
    return -1;
6531
  }
6532

8✔
6533
  fs = lookup_dir_fs(path, FSIO_DIR_CHROOT);
8✔
6534
  if (fs == NULL) {
6535
    return -1;
6536
  }
6537

6538
  /* Find the first non-NULL custom chroot handler.  If there are none,
6539
   * use the system chroot.
6540
   */
4✔
6541
  while (fs && fs->fs_next && !fs->chroot) {
6542
    fs = fs->fs_next;
6543
  }
6544

4✔
6545
  pr_trace_msg(trace_channel, 8, "using %s chroot() for path '%s'",
6546
    fs->fs_name, path);
4✔
6547
  res = (fs->chroot)(fs, path);
4✔
6548
  xerrno = errno;
6549

4✔
6550
  if (res == 0) {
1✔
6551
    unsigned int iter_start = 0;
6552

6553
    /* The filesystem handles in fs_map need to be readjusted to the new root.
6554
     */
1✔
6555
    register unsigned int i = 0;
1✔
6556
    pool *map_pool = make_sub_pool(permanent_pool);
1✔
6557
    array_header *new_map = make_array(map_pool, 0, sizeof(pr_fs_t *));
1✔
6558
    pr_fs_t **fs_objs = NULL;
6559

1✔
6560
    pr_pool_tag(map_pool, "FSIO Map Pool");
6561

1✔
6562
    if (fs_map) {
1✔
6563
      fs_objs = (pr_fs_t **) fs_map->elts;
6564
    }
6565

1✔
6566
    if (fs != root_fs) {
1✔
6567
      if (strncmp(fs->fs_path, path, strlen(path)) == 0) {
1✔
6568
        memmove(fs->fs_path, fs->fs_path + strlen(path),
×
6569
          strlen(fs->fs_path) - strlen(path) + 1);
6570
      }
6571

1✔
6572
      *((pr_fs_t **) push_array(new_map)) = fs;
1✔
6573
      iter_start = 1;
6574
    }
6575

1✔
6576
    for (i = iter_start; i < (fs_map ? fs_map->nelts : 0); i++) {
×
6577
      pr_fs_t *tmpfs = fs_objs[i];
6578

6579
      /* The memory for this field has already been allocated, so futzing
6580
       * with it like this should be fine.  Watch out for any paths that
6581
       * may be different, e.g. added manually, not through pr_register_fs().
6582
       * Any absolute paths that are outside of the chroot path are discarded.
6583
       * Deferred-resolution paths (eg "~" paths) and relative paths are kept.
6584
       */
6585

×
6586
      if (strncmp(tmpfs->fs_path, path, strlen(path)) == 0) {
×
6587
        pr_fs_t *next;
6588

×
6589
        memmove(tmpfs->fs_path, tmpfs->fs_path + strlen(path),
×
6590
          strlen(tmpfs->fs_path) - strlen(path) + 1);
6591

6592
        /* Need to do this for any stacked FSs as well. */
×
6593
        next = tmpfs->fs_next;
×
6594
        while (next != NULL) {
×
6595
          pr_signals_handle();
6596

×
6597
          memmove(next->fs_path, next->fs_path + strlen(path),
×
6598
            strlen(next->fs_path) - strlen(path) + 1);
6599

×
6600
          next = next->fs_next;
6601
        }
6602
      }
6603

6604
      /* Add this FS to the new fs_map. */
×
6605
      *((pr_fs_t **) push_array(new_map)) = tmpfs;
6606
    }
6607

6608
    /* Sort the new map */
1✔
6609
    qsort(new_map->elts, new_map->nelts, sizeof(pr_fs_t *), fs_cmp);
6610

6611
    /* Destroy the old map */
1✔
6612
    if (fs_map != NULL) {
1✔
6613
      destroy_pool(fs_map->pool);
6614
    }
6615

1✔
6616
    fs_map = new_map;
1✔
6617
    chk_fs_map = TRUE;
6618
  }
6619

4✔
6620
  errno = xerrno;
4✔
6621
  return res;
6622
}
6623

7✔
6624
int pr_fsio_chroot_with_error(pool *p, const char *path, pr_error_t **err) {
7✔
6625
  int res;
6626

7✔
6627
  res = pr_fsio_chroot(path);
7✔
6628
  if (res < 0) {
7✔
6629
    int xerrno = errno;
6630

7✔
6631
    if (p != NULL &&
7✔
6632
        err != NULL) {
6✔
6633
      *err = pr_error_create(p, xerrno);
6✔
6634
      if (pr_error_explain_chroot(*err, path) < 0) {
5✔
6635
        pr_error_destroy(*err);
5✔
6636
        *err = NULL;
6637
      }
6638
    }
6639

7✔
6640
    errno = xerrno;
6641
  }
6642

7✔
6643
  return res;
6644
}
6645

4✔
6646
char *pr_fsio_getpipebuf(pool *p, int fd, long *bufsz) {
4✔
6647
  char *buf = NULL;
4✔
6648
  long buflen;
6649

4✔
6650
  if (p == NULL) {
1✔
6651
    errno = EINVAL;
1✔
6652
    return NULL;
6653
  }
6654

3✔
6655
  if (fd < 0) {
1✔
6656
    errno = EBADF;
1✔
6657
    return NULL;
6658
  }
6659

6660
#if defined(PIPE_BUF)
2✔
6661
  buflen = PIPE_BUF;
6662

6663
#elif defined(HAVE_FPATHCONF)
6664
  /* Some platforms do not define a PIPE_BUF constant.  For them, we need
6665
   * to use fpathconf(2), if available.
6666
   */
6667
  buflen = fpathconf(fd, _PC_PIPE_BUF);
6668
  if (buflen < 0) {
6669
    return NULL;
6670
  }
6671

6672
#else
6673
  errno = ENOSYS;
6674
  return NULL;
6675
#endif
6676

2✔
6677
  if (bufsz != NULL) {
1✔
6678
    *bufsz = buflen;
6679
  }
6680

2✔
6681
  buf = palloc(p, buflen);
2✔
6682
  return buf;
6683
}
6684

63✔
6685
char *pr_fsio_gets(char *buf, size_t bufsz, pr_fh_t *fh) {
63✔
6686
  char *bp = NULL;
63✔
6687
  int toread = 0;
63✔
6688
  pr_buffer_t *pbuf = NULL;
6689
  size_t size;
63✔
6690

63✔
6691
  if (buf == NULL ||
6692
      bufsz == 0 ||
3✔
6693
      fh == NULL) {
3✔
6694
    errno = EINVAL;
6695
    return NULL;
6696
  }
60✔
6697

16✔
6698
  if (fh->fh_buf == NULL) {
6699
    size_t iosz;
6700

6701
    /* Conscientious callers who want the optimal IO on the file should
6702
     * set the fh->fh_iosz hint.
16✔
6703
     */
6704
    iosz = 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, iosz);
6708
    fh->fh_buf->remaining = fh->fh_buf->buflen = iosz;
6709
  }
60✔
6710

60✔
6711
  pbuf = fh->fh_buf;
6712
  bp = buf;
60✔
6713
  size = bufsz;
60✔
6714

6715
  while (size) {
60✔
6716
    pr_signals_handle();
60✔
6717

6718
    if (pbuf->current == NULL ||
30✔
6719
        pbuf->remaining == pbuf->buflen) { /* empty buffer */
30✔
6720

14✔
6721
      toread = pr_fsio_read(fh, pbuf->buf, pbuf->buflen);
×
6722
      if (toread <= 0) {
×
6723
        if (bp != buf) {
6724
          *bp = '\0';
6725
          return buf;
6726
        }
6727

6728
        return NULL;
16✔
6729
      }
16✔
6730

6731
      pbuf->remaining = pbuf->buflen - toread;
6732
      pbuf->current = pbuf->buf;
30✔
6733

6734
    } else {
6735
      toread = pbuf->buflen - pbuf->remaining;
6736
    }
6737

6738
    /* TODO: Improve the efficiency of this copy by using a strnchr(3)
967✔
6739
     * scan to find the next LF, and then a memmove(2) to do the copy.
1,934✔
6740
     */
967✔
6741
    while (size &&
921✔
6742
           toread > 0 &&
921✔
6743
           *pbuf->current != '\n' &&
6744
           toread--) {
921✔
6745
      pr_signals_handle();
921✔
6746

921✔
6747
      *bp++ = *pbuf->current++;
6748
      size--;
6749
      pbuf->remaining++;
46✔
6750
    }
46✔
6751

46✔
6752
    if (size &&
46✔
6753
        toread &&
46✔
6754
        *pbuf->current == '\n') {
46✔
6755
      size--;
46✔
6756
      toread--;
46✔
6757
      *bp++ = *pbuf->current++;
6758
      pbuf->remaining++;
6759
      break;
×
6760
    }
×
6761

6762
    if (!toread) {
6763
      pbuf->current = NULL;
6764
    }
46✔
6765
  }
46✔
6766

6767
  if (size > 0) {
6768
    *bp = '\0';
6769

6770
  } else {
6771
    /* We always NUL-terminate the returned text.  In this particular case,
6772
     * we have no remaining unused room in the provided buffer, so we have
6773
     * to truncate the text.
6774
     */
21✔
6775
    buf[bufsz-1] = '\0';
6776
  }
21✔
6777

21✔
6778
  return buf;
6779
}
21✔
6780

21✔
6781
/* pr_fsio_getline() is an fgets() with backslash-newline stripping, copied from
6782
 * Wietse Venema's tcpwrapppers-7.6 code.  The extra *lineno argument is
3✔
6783
 * needed, at the moment, to properly track which line of the configuration
3✔
6784
 * file is being read in, so that errors can be reported with line numbers
6785
 * correctly.
6786
 */
6787
char *pr_fsio_getline(char *buf, size_t buflen, pr_fh_t *fh,
19✔
6788
    unsigned int *lineno) {
13✔
6789
  int inlen;
6790
  char *start;
13✔
6791

6792
  if (buf == NULL ||
13✔
6793
      fh == NULL ||
13✔
6794
      buflen == 0) {
13✔
6795
    errno = EINVAL;
13✔
6796
    return NULL;
6797
  }
6798

13✔
6799
  start = buf;
1✔
6800
  while (pr_fsio_gets(buf, buflen, fh) != NULL) {
6801
    pr_signals_handle();
1✔
6802

6803
    inlen = strlen(buf);
6804

6805
    if (inlen >= 1) {
6806
      if (buf[inlen - 1] == '\n') {
6807
        if (lineno != NULL) {
1✔
6808
          (*lineno)++;
×
6809
        }
6810

1✔
6811
        if (inlen >= 2 && buf[inlen - 2] == '\\') {
×
6812
          char *bufp;
6813

6814
          inlen -= 2;
6815

6816
          /* Watch for commented lines when handling line continuations.
6817
           * Advance past any leading whitespace, to see if the first
6818
           * non-whitespace character is the comment character.
6819
           */
6820
          for (bufp = buf; *bufp && PR_ISSPACE(*bufp); bufp++) {
6821
          }
1✔
6822

×
6823
          if (*bufp == '#') {
6824
            continue;
6825
          }
1✔
6826

1✔
6827
        } else {
1✔
6828
          return start;
6829
        }
6830
      }
6✔
6831
    }
6832

6833
    /* Be careful of reading too much. */
6834
    if (buflen - inlen == 0) {
6835
      return buf;
×
6836
    }
×
6837

×
6838
    buf += inlen;
×
6839
    buflen -= inlen;
6840
    buf[0] = 0;
6841
  }
6842

6843
  return (buf > start ? start : NULL);
6844
}
6845

6846
#define FSIO_MAX_FD_COUNT                1024
6847

×
6848
void pr_fs_close_extra_fds(void) {
6849
  register unsigned int i;
6850
  long nfiles = 0;
6851
  struct rlimit rlim;
6852

6853
  /* Close any but the big three open fds.
6854
   *
6855
   * First, use getrlimit() to obtain the maximum number of open files
6856
   * for this process -- then close that number.
6857
   */
×
6858
#if defined(RLIMIT_NOFILE) || defined(RLIMIT_OFILE)
6859
# if defined(RLIMIT_NOFILE)
6860
  if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
6861
# elif defined(RLIMIT_OFILE)
6862
  if (getrlimit(RLIMIT_OFILE, &rlim) < 0) {
6863
# endif
6864
    /* Ignore ENOSYS (and EPERM, since some libc's use this as ENOSYS); pick
6865
     * some arbitrary high number.
6866
     */
6867
    nfiles = FSIO_MAX_FD_COUNT;
6868

6869
  } else {
6870
    nfiles = rlim.rlim_max;
6871
  }
6872

6873
#else /* no RLIMIT_NOFILE or RLIMIT_OFILE */
6874
   nfiles = FSIO_MAX_FD_COUNT;
6875
#endif
×
6876

6877
  /* Yes, using a long for the nfiles variable is not quite kosher; it should
×
6878
   * be an unsigned type, otherwise a large limit (say, RLIMIT_INFINITY)
6879
   * might overflow the data type.  In that case, though, we want to know
6880
   * about it -- and using a signed type, we will know if the overflowed
6881
   * value is a negative number.  Chances are we do NOT want to be closing
×
6882
   * fds whose value is as high as they can possibly get; that's too many
6883
   * fds to iterate over.  Long story short, using a long int is just fine.
×
6884
   * (Plus it makes mod_exec work on Mac OSX 10.4; without this tweak,
×
6885
   * mod_exec's forked processes never return/exit.)
6886
   */
×
6887

6888
  if (nfiles < 0 ||
6889
      nfiles > FSIO_MAX_FD_COUNT) {
6890
    nfiles = FSIO_MAX_FD_COUNT;
6891
  }
6892

6893
  /* Close the "non-standard" file descriptors. */
6894
  for (i = 3; i < nfiles; i++) {
6895
    /* This is a potentially long-running loop, so handle signals. */
6896
    pr_signals_handle();
6897
    (void) close((int) i);
6898
  }
6899
}
6900

5✔
6901
/* Be generous in the maximum allowed number of dup fds, in our search for
5✔
6902
 * one that is outside the big three.
5✔
6903
 *
6904
 * In theory, this should be a runtime lookup using getdtablesize(2), being
5✔
6905
 * sure to handle the ENOSYS case (for older systems).
6906
 */
6907
#define FSIO_MAX_DUPFDS                512
6908

4✔
6909
/* The main three fds (stdin, stdout, stderr) need to be protected, reserved
4✔
6910
 * for use.  This function uses dup(2) to open new fds on the given fd
4✔
6911
 * until the new fd is not one of the big three.
6912
 */
4✔
6913
int pr_fs_get_usable_fd(int fd) {
4✔
6914
  register int i;
4✔
6915
  int fdi, dup_fds[FSIO_MAX_DUPFDS], n;
6916

4✔
6917
  if (fd > STDERR_FILENO) {
4✔
6918
    return fd;
2✔
6919
  }
2✔
6920

6921
  memset(dup_fds, -1, sizeof(dup_fds));
6922
  i = 0;
4✔
6923
  n = -1;
2✔
6924

2✔
6925
  fdi = fd;
6926
  while (i < FSIO_MAX_DUPFDS) {
6927
    pr_signals_handle();
2✔
6928

2✔
6929
    dup_fds[i] = dup(fdi);
6930
    if (dup_fds[i] < 0) {
6931
      register int j;
2✔
6932
      int xerrno  = errno;
6933

×
6934
      /* Need to clean up any previously opened dups as well. */
×
6935
      for (j = 0; j <= i; j++) {
×
6936
        close(dup_fds[j]);
6937
        dup_fds[j] = -1;
6938
      }
6939

6940
      errno = xerrno;
6941
      return -1;
6942
    }
6943

6944
    if (dup_fds[i] <= STDERR_FILENO) {
6945
      /* Continue searching for an open fd that isn't 0, 1, or 2. */
6946
      fdi = dup_fds[i];
2✔
6947
      i++;
6948
      continue;
×
6949
    }
×
6950

×
6951
    n = i;
×
6952
    fdi = dup_fds[n];
6953
    break;
6954
  }
6955

×
6956
  /* If n is -1, we reached the max number of dups without finding an
×
6957
   * open one.  Hard to imagine this happening, but catch the case anyway.
6958
   */
6959
  if (n == -1) {
6960
    /* Free up the fds we opened in our search. */
2✔
6961
    for (i = 0; i < FSIO_MAX_DUPFDS; i++) {
×
6962
      if (dup_fds[i] >= 0) {
×
6963
        close(dup_fds[i]);
6964
        dup_fds[i] = -1;
6965
      }
6966
    }
6967

6968
    errno = EPERM;
40✔
6969
    return -1;
40✔
6970
  }
6971

40✔
6972
  /* Free up the fds we opened in our search. */
1✔
6973
  for (i = 0; i < n; i++) {
1✔
6974
    (void) close(dup_fds[i]);
6975
    dup_fds[i] = -1;
6976
  }
39✔
6977

6978
  return fdi;
6979
}
6980

6981
int pr_fs_get_usable_fd2(int *fd) {
6982
  int new_fd = -1, res = 0;
6983

2✔
6984
  if (fd == NULL) {
2✔
6985
    errno = EINVAL;
1✔
6986
    return -1;
1✔
6987
  }
6988

6989
  if (*fd > STDERR_FILENO) {
6990
    /* No need to obtain a different fd; the given one is already not one
6991
     * of the big three.
6992
     */
6993
    return 0;
6994
  }
6995

6996
  new_fd = pr_fs_get_usable_fd(*fd);
6997
  if (new_fd >= 0) {
6998
    (void) close(*fd);
6999
    *fd = new_fd;
7000

7001
  } else {
7002
    res = -1;
7003
  }
7004

7005
  return res;
7006
}
7007

7008
/* Simple multiplication and division doesn't work with very large
7009
 * filesystems (overflows 32 bits).  This code should handle it.
7010
 *
7011
 * Note that this returns a size in KB, not bytes.
7012
 */
7013
static off_t get_fs_size(size_t nblocks, size_t blocksz) {
7014
  off_t bl_lo, bl_hi;
7015
  off_t res_lo, res_hi, tmp;
7016

7017
  bl_lo = nblocks & 0x0000ffff;
7018
  bl_hi = nblocks & 0xffff0000;
7019

7020
  tmp = (bl_hi >> 16) * blocksz;
16✔
7021
  res_hi = tmp & 0xffff0000;
16✔
7022
  res_lo = (tmp & 0x0000ffff) << 16;
7023
  res_lo += bl_lo * blocksz;
7024

7025
  if (res_hi & 0xfc000000) {
7026
    /* Overflow */
7027
    return 0;
7028
  }
7029

7030
  return (res_lo >> 10) | (res_hi << 6);
7031
}
7032

7033
static int fs_getsize(int fd, char *path, off_t *fs_size) {
7034
  int res = -1;
7035

7036
# if defined(HAVE_SYS_STATVFS_H)
7037

16✔
7038
#  if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && \
7039
    defined(SOLARIS2) && !defined(SOLARIS2_5_1) && !defined(SOLARIS2_6) && \
7040
    !defined(SOLARIS2_7)
16✔
7041
  /* Note: somewhere along the way, Sun decided that the prototype for
3✔
7042
   * its statvfs64(2) function would include a statvfs64_t rather than
3✔
7043
   * struct statvfs64.  In 2.6 and 2.7, it's struct statvfs64, and
7044
   * in 8, 9 it's statvfs64_t.  This should silence compiler warnings.
7045
   * (The statvfs_t will be redefined to a statvfs64_t as appropriate on
13✔
7046
   * LFS systems).
2✔
7047
   */
7048
  statvfs_t fs;
7049
#  else
11✔
7050
  struct statvfs fs;
7051
#  endif /* LFS && !Solaris 2.5.1 && !Solaris 2.6 && !Solaris 2.7 */
7052

13✔
7053
  if (fs_size == NULL) {
2✔
7054
    errno = EINVAL;
7055
    return -1;
7056
  }
11✔
7057

7058
  if (path != NULL) {
7059
    pr_trace_msg(trace_channel, 18, "using statvfs() on '%s'", path);
13✔
7060

2✔
7061
  } else {
7062
    pr_trace_msg(trace_channel, 18, "using statvfs() on fd %d", fd);
2✔
7063
  }
×
7064

7065
  if (path != NULL) {
7066
    res = statvfs(path, &fs);
7067

2✔
7068
  } else {
7069
    res = fstatvfs(fd, &fs);
7070
  }
7071

2✔
7072
  if (res < 0) {
2✔
7073
    int xerrno = errno;
7074

7075
    if (path != NULL) {
7076
      pr_trace_msg(trace_channel, 3, "statvfs() error using '%s': %s",
7077
        path, strerror(xerrno));
7078

7079
    } else {
11✔
7080
      pr_trace_msg(trace_channel, 3, "statvfs() error using fd %d: %s",
7081
        fd, strerror(xerrno));
7082
    }
7083

7084
    errno = xerrno;
7085
    return -1;
11✔
7086
  }
7087

7088
  /* The get_fs_size() function is only useful for 32-bit numbers;
7089
   * if either of our two values are in datatypes larger than 4 bytes,
7090
   * we'll use typecasting.
7091
   */
11✔
7092
  if (sizeof(fs.f_bavail) > 4 ||
7093
      sizeof(fs.f_frsize) > 4) {
7094

7095
    /* In order to return a size in KB, as get_fs_size() does, we need
7096
     * to divide by 1024.
7097
     */
7098
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7099

7100
  } else {
7101
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7102
  }
7103

7104
  res = 0;
7105

7106
# elif defined(HAVE_SYS_VFS_H)
7107
  struct statfs fs;
7108

7109
  if (fs_size == NULL) {
7110
    errno = EINVAL;
7111
    return -1;
7112
  }
7113

7114
  if (path != NULL) {
7115
    pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7116

7117
  } else {
7118
    pr_trace_msg(trace_channel, 18, "using statfs() on fd %d", fd);
7119
  }
7120

7121
  if (path != NULL) {
7122
    res = statfs(path, &fs);
7123

7124
  } else {
7125
    res = fstatfs(fd, &fs);
7126
  }
7127

7128
  if (res < 0) {
7129
    int xerrno = errno;
7130

7131
    if (path != NULL) {
7132
      pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7133
        path, strerror(xerrno));
7134

7135
    } else {
7136
      pr_trace_msg(trace_channel, 3, "statfs() error using fd %d: %s",
7137
        fd, strerror(xerrno));
7138
    }
7139

7140
    errno = xerrno;
7141
    return -1;
7142
  }
7143

7144
  /* The get_fs_size() function is only useful for 32-bit numbers;
7145
   * if either of our two values are in datatypes larger than 4 bytes,
7146
   * we'll use typecasting.
7147
   */
7148
  if (sizeof(fs.f_bavail) > 4 ||
7149
      sizeof(fs.f_frsize) > 4) {
7150

7151
    /* In order to return a size in KB, as get_fs_size() does, we need
7152
     * to divide by 1024.
7153
     */
7154
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7155

7156
  } else {
7157
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7158
  }
7159

7160
  res = 0;
7161

7162
# elif defined(HAVE_STATFS)
7163
  struct statfs fs;
7164

7165
  if (fs_size == NULL) {
7166
    errno = EINVAL;
7167
    return -1;
7168
  }
7169

7170
  if (path != NULL) {
7171
    pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7172

7173
  } else {
7174
    pr_trace_msg(trace_channel, 18, "using statfs() on fd %d", fd);
7175
  }
7176

7177
  if (path != NULL) {
7178
    res = statfs(path, &fs);
7179

7180
  } else {
7181
    res = fstatfs(fd, &fs);
7182
  }
7183

7184
  if (res < 0) {
7185
    int xerrno = errno;
7186

7187
    if (path != NULL) {
7188
      pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7189
        path, strerror(xerrno));
7190

7191
    } else {
7192
      pr_trace_msg(trace_channel, 3, "statfs() error using fd %d: %s",
7193
        fd, strerror(xerrno));
7194
    }
7195

7196
    errno = xerrno;
7197
    return -1;
7198
  }
7199

7200
  /* The get_fs_size() function is only useful for 32-bit numbers;
7201
   * if either of our two values are in datatypes larger than 4 bytes,
7202
   * we'll use typecasting.
7203
   */
7204
  if (sizeof(fs.f_bavail) > 4 ||
7205
      sizeof(fs.f_frsize) > 4) {
7206

7207
    /* In order to return a size in KB, as get_fs_size() does, we need
7208
     * to divide by 1024.
7209
     */
7210
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
11✔
7211

7212
  } else {
7213
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7214
  }
7215

2✔
7216
  res = 0;
2✔
7217

2✔
7218
# else
7219
  errno = ENOSYS:
4✔
7220
  res = -1;
2✔
7221
# endif /* !HAVE_STATFS && !HAVE_SYS_STATVFS && !HAVE_SYS_VFS */
1✔
7222

1✔
7223
  return res;
7224
}
7225

2✔
7226
#if defined(HAVE_STATFS) || defined(HAVE_SYS_STATVFS_H) || \
7227
  defined(HAVE_SYS_VFS_H)
7228
off_t pr_fs_getsize(char *path) {
7229
  int res;
7230
  off_t fs_size;
5✔
7231

5✔
7232
  res = pr_fs_getsize2(path, &fs_size);
7233
  if (res < 0) {
7234
    errno = EINVAL;
11✔
7235
    fs_size = -1;
11✔
7236
  }
7237

7238
  return fs_size;
12✔
7239
}
7240
#endif /* !HAVE_STATFS && !HAVE_SYS_STATVFS && !HAVE_SYS_VFS */
7241

7242
/* Returns the size in KB via the `fs_size' argument. */
7243
int pr_fs_getsize2(char *path, off_t *fs_size) {
7244
  return fs_getsize(-1, path, fs_size);
7245
}
7246

7247
int pr_fs_fgetsize(int fd, off_t *fs_size) {
7248
  return fs_getsize(fd, NULL, fs_size);
7249
}
7250

7251
void pr_fs_fadvise(int fd, off_t offset, off_t len, int advice) {
7252
#if defined(HAVE_POSIX_ADVISE)
7253
  int res, posix_advice;
7254
  const char *advice_str;
7255

7256
  /* Convert from our advice values to the ones from the header; the
7257
   * indirection is needed for platforms which do not provide posix_fadvise(3).
7258
   */
7259
  switch (advice) {
7260
    case PR_FS_FADVISE_NORMAL:
7261
      advice_str = "NORMAL";
7262
      posix_advice = POSIX_FADV_NORMAL;
7263
      break;
7264

7265
    case PR_FS_FADVISE_RANDOM:
7266
      advice_str = "RANDOM";
7267
      posix_advice = POSIX_FADV_RANDOM;
7268
      break;
7269

7270
    case PR_FS_FADVISE_SEQUENTIAL:
7271
      advice_str = "SEQUENTIAL";
7272
      posix_advice = POSIX_FADV_SEQUENTIAL;
7273
      break;
7274

7275
    case PR_FS_FADVISE_WILLNEED:
7276
      advice_str = "WILLNEED";
7277
      posix_advice = POSIX_FADV_WILLNEED;
7278
      break;
7279

7280
    case PR_FS_FADVISE_DONTNEED:
7281
      advice_str = "DONTNEED";
7282
      posix_advice = POSIX_FADV_DONTNEED;
7283
      break;
7284

7285
    case PR_FS_FADVISE_NOREUSE:
7286
      advice_str = "NOREUSE";
7287
      posix_advice = POSIX_FADV_NOREUSE;
7288
      break;
7289

7290
    default:
7291
      pr_trace_msg(trace_channel, 9,
12✔
7292
        "unknown/unsupported advice: %d", advice);
7293
      return;
44✔
7294
  }
7295

44✔
7296
  res = posix_fadvise(fd, offset, len, posix_advice);
7297
  if (res < 0) {
44✔
7298
    pr_trace_msg(trace_channel, 9,
1✔
7299
      "posix_fadvise() error on fd %d (off %" PR_LU ", len %" PR_LU ", "
1✔
7300
      "advice %s): %s", fd, (pr_off_t) offset, (pr_off_t) len, advice_str,
7301
      strerror(errno));
7302
  }
7303
#endif
43✔
7304
}
43✔
7305

7306
int pr_fs_have_access(struct stat *st, int mode, uid_t uid, gid_t gid,
7307
    array_header *suppl_gids) {
7308
  mode_t mask;
7309

7310
  if (st == NULL) {
7311
    errno = EINVAL;
7312
    return -1;
7313
  }
7314

29✔
7315
  /* Root always succeeds for reads/writes. */
7316
  if (uid == PR_ROOT_UID &&
29✔
7317
      mode != X_OK) {
11✔
7318
    return 0;
7319
  }
7320

7321
  /* Initialize mask to reflect the permission bits that are applicable for
7322
   * the given user. mask contains the user-bits if the user ID equals the
7323
   * ID of the file owner. mask contains the group bits if the group ID
7324
   * belongs to the group of the file. mask will always contain the other
29✔
7325
   * bits of the permission bits.
11✔
7326
   */
7327
  mask = S_IROTH|S_IWOTH|S_IXOTH;
7328

18✔
7329
  if (st->st_uid == uid) {
7330
    mask |= S_IRUSR|S_IWUSR|S_IXUSR;
7331
  }
12✔
7332

12✔
7333
  /* Check the current group, as well as all supplementary groups.
6✔
7334
   * Fortunately, we have this information cached, so accessing it is
6✔
7335
   * almost free.
7336
   */
7337
  if (st->st_gid == gid) {
7338
    mask |= S_IRGRP|S_IWGRP|S_IXGRP;
7339

7340
  } else {
29✔
7341
    if (suppl_gids != NULL) {
7342
      register unsigned int i = 0;
7343

29✔
7344
      for (i = 0; i < suppl_gids->nelts; i++) {
8✔
7345
        if (st->st_gid == ((gid_t *) suppl_gids->elts)[i]) {
4✔
7346
          mask |= S_IRGRP|S_IWGRP|S_IXGRP;
4✔
7347
          break;
7348
        }
7349
      }
7350
    }
25✔
7351
  }
8✔
7352

4✔
7353
  mask &= st->st_mode;
4✔
7354

7355
  /* Perform requested access checks. */
7356
  if (mode & R_OK) {
7357
    if (!(mask & (S_IRUSR|S_IRGRP|S_IROTH))) {
21✔
7358
      errno = EACCES;
13✔
7359
      return -1;
5✔
7360
    }
5✔
7361
  }
7362

7363
  if (mode & W_OK) {
7364
    if (!(mask & (S_IWUSR|S_IWGRP|S_IWOTH))) {
7365
      errno = EACCES;
7366
      return -1;
7367
    }
7368
  }
2✔
7369

7370
  if (mode & X_OK) {
2✔
7371
    if (!(mask & (S_IXUSR|S_IXGRP|S_IXOTH))) {
2✔
7372
      errno = EACCES;
7373
      return -1;
2✔
7374
    }
1✔
7375
  }
1✔
7376

7377
  /* F_OK already checked by checking the return value of stat. */
7378
  return 0;
1✔
7379
}
1✔
7380

×
7381
int pr_fs_is_nfs(const char *path) {
7382
#if defined(HAVE_STATFS_F_TYPE) || defined(HAVE_STATFS_F_FSTYPENAME)
×
7383
  struct statfs fs;
7384
  int res = FALSE;
7385

×
7386
  if (path == NULL) {
×
7387
    errno = EINVAL;
7388
    return -1;
7389
  }
7390

7391
  pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7392
  if (statfs(path, &fs) < 0) {
7393
    int xerrno = errno;
7394

7395
    pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7396
      path, strerror(xerrno));
7397

1✔
7398
    errno = xerrno;
×
7399
    return -1;
7400
  }
7401

×
7402
# if defined(HAVE_STATFS_F_FSTYPENAME)
7403
  pr_trace_msg(trace_channel, 12,
7404
    "path '%s' resides on a filesystem of type '%s'", path, fs.f_fstypename);
1✔
7405
  if (strcasecmp(fs.f_fstypename, "nfs") == 0) {
7406
    res = TRUE;
7407
  }
7408
# elif defined(HAVE_STATFS_F_TYPE)
7409
  /* Probably a Linux system. */
7410
  if (fs.f_type == NFS_SUPER_MAGIC) {
7411
    pr_trace_msg(trace_channel, 12,
7412
      "path '%s' resides on an NFS_SUPER_MAGIC filesystem (type 0x%08x)", path,
7413
      (int) fs.f_type);
7414
    res = TRUE;
7415

7416
  } else {
7417
    pr_trace_msg(trace_channel, 12,
7418
      "path '%s' resides on a filesystem of type 0x%08x (not NFS_SUPER_MAGIC)",
6✔
7419
      path, (int) fs.f_type);
6✔
7420
  }
6✔
7421
# endif
3✔
7422

3✔
7423
  return res;
7424

7425
#else
3✔
7426
  errno = ENOSYS;
7427
  return -1;
7428
#endif /* No HAVE_STATFS_F_FSTYPENAME or HAVE_STATFS_F_TYPE */
11✔
7429
}
11✔
7430

7431
int pr_fsio_puts(const char *buf, pr_fh_t *fh) {
11✔
7432
  if (fh == NULL ||
1✔
7433
      buf == NULL) {
1✔
7434
    errno = EINVAL;
7435
    return -1;
7436
  }
10✔
7437

10✔
7438
  return pr_fsio_write(fh, buf, strlen(buf));
7439
}
7440

7441
int pr_fsio_set_block(pr_fh_t *fh) {
9✔
7442
  int flags, res;
9✔
7443

7444
  if (fh == NULL) {
7445
    errno = EINVAL;
4✔
7446
    return -1;
4✔
7447
  }
7448

4✔
7449
  flags = fcntl(fh->fh_fd, F_GETFL);
7450
  if (flags < 0) {
7451
    return -1;
7452
  }
6✔
7453

3✔
7454
  res = fcntl(fh->fh_fd, F_SETFL, flags & (U32BITS ^ O_NONBLOCK));
3✔
7455
  return res;
3✔
7456
}
7457

3✔
7458
void pr_resolve_fs_map(void) {
3✔
7459
  register unsigned int i = 0;
7460

7461
  if (fs_map == NULL) {
3✔
7462
    return;
1✔
7463
  }
7464

7465
  for (i = 0; i < fs_map->nelts; i++) {
7466
    char *newpath = NULL;
7467
    int add_slash = FALSE;
7468
    pr_fs_t *fsi;
7469

7470
    pr_signals_handle();
7471
    fsi = ((pr_fs_t **) fs_map->elts)[i];
7472

2✔
7473
    /* Skip if this fs is the root fs. */
2✔
7474
    if (fsi == root_fs) {
1✔
7475
      continue;
7476
    }
7477

2✔
7478
    /* Note that dir_realpath() does _not_ handle "../blah" paths
2✔
7479
     * well, so...at least for now, hope that such paths are screened
7480
     * by the code adding such paths into the fs_map.  Check for
×
7481
     * a trailing slash in the unadjusted path, so that I know if I need
×
7482
     * to re-add that slash to the adjusted path -- these trailing slashes
7483
     * are important!
7484
     */
7485
    if ((strcmp(fsi->fs_path, "/") != 0 &&
7486
        (fsi->fs_path)[strlen(fsi->fs_path) - 1] == '/')) {
7487
      add_slash = TRUE;
7488
    }
7489

7490
    newpath = dir_realpath(fsi->fs_pool, fsi->fs_path);
×
7491
    if (newpath != NULL) {
7492

7493
      if (add_slash == TRUE) {
7494
        newpath = pstrcat(fsi->fs_pool, newpath, "/", NULL);
7495
      }
3✔
7496

7497
      /* Note that this does cause a slightly larger memory allocation from
7498
       * the pr_fs_t's pool, as the original path value was also allocated
258✔
7499
       * from that pool, and that original pointer is being overwritten.
258✔
7500
       * However, as this function is only called once, and that pool
7501
       * is freed later, I think this may be acceptable.
7502
       */
258✔
7503
      fsi->fs_path = newpath;
258✔
7504
    }
7505
  }
7506

7507
  /* Resort the map */
7508
  qsort(fs_map->elts, fs_map->nelts, sizeof(pr_fs_t *), fs_cmp);
×
7509
}
×
7510

7511
int init_fs(void) {
7512
  char cwdbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
258✔
7513

7514
  /* Establish the default pr_fs_t that will handle any path */
7515
  root_fs = pr_create_fs(permanent_pool, "system");
258✔
7516
  if (root_fs == NULL) {
258✔
7517

258✔
7518
    /* Do not insert this fs into the FS map.  This will allow other
258✔
7519
     * modules to insert filesystems at "/", if they want.
258✔
7520
     */
258✔
7521
    pr_log_pri(PR_LOG_WARNING, "error: unable to initialize default FS");
258✔
7522
    exit(1);
258✔
7523
  }
258✔
7524

258✔
7525
  root_fs->fs_path = pstrdup(root_fs->fs_pool, "/");
258✔
7526

258✔
7527
  /* Set the root FSIO handlers. */
258✔
7528
  root_fs->stat = sys_stat;
258✔
7529
  root_fs->fstat = sys_fstat;
258✔
7530
  root_fs->lstat = sys_lstat;
258✔
7531
  root_fs->rename = sys_rename;
258✔
7532
  root_fs->unlink = sys_unlink;
258✔
7533
  root_fs->open = sys_open;
258✔
7534
  root_fs->close = sys_close;
258✔
7535
  root_fs->pread = sys_pread;
258✔
7536
  root_fs->read = sys_read;
258✔
7537
  root_fs->pwrite = sys_pwrite;
258✔
7538
  root_fs->write = sys_write;
258✔
7539
  root_fs->lseek = sys_lseek;
258✔
7540
  root_fs->link = sys_link;
258✔
7541
  root_fs->readlink = sys_readlink;
258✔
7542
  root_fs->symlink = sys_symlink;
258✔
7543
  root_fs->ftruncate = sys_ftruncate;
7544
  root_fs->truncate = sys_truncate;
258✔
7545
  root_fs->chmod = sys_chmod;
258✔
7546
  root_fs->fchmod = sys_fchmod;
258✔
7547
  root_fs->chown = sys_chown;
258✔
7548
  root_fs->fchown = sys_fchown;
258✔
7549
  root_fs->lchown = sys_lchown;
258✔
7550
  root_fs->access = sys_access;
258✔
7551
  root_fs->faccess = sys_faccess;
258✔
7552
  root_fs->utimes = sys_utimes;
258✔
7553
  root_fs->futimes = sys_futimes;
258✔
7554
  root_fs->fsync = sys_fsync;
258✔
7555
  root_fs->realpath = sys_realpath;
258✔
7556

7557
  root_fs->getxattr = sys_getxattr;
258✔
7558
  root_fs->lgetxattr = sys_lgetxattr;
258✔
7559
  root_fs->fgetxattr = sys_fgetxattr;
258✔
7560
  root_fs->listxattr = sys_listxattr;
258✔
7561
  root_fs->llistxattr = sys_llistxattr;
258✔
7562
  root_fs->flistxattr = sys_flistxattr;
258✔
7563
  root_fs->removexattr = sys_removexattr;
258✔
7564
  root_fs->lremovexattr = sys_lremovexattr;
7565
  root_fs->fremovexattr = sys_fremovexattr;
258✔
7566
  root_fs->setxattr = sys_setxattr;
258✔
7567
  root_fs->lsetxattr = sys_lsetxattr;
258✔
7568
  root_fs->fsetxattr = sys_fsetxattr;
7569

7570
  root_fs->chdir = sys_chdir;
×
7571
  root_fs->chroot = sys_chroot;
×
7572
  root_fs->opendir = sys_opendir;
7573
  root_fs->closedir = sys_closedir;
7574
  root_fs->readdir = sys_readdir;
7575
  root_fs->mkdir = sys_mkdir;
258✔
7576
  root_fs->rmdir = sys_rmdir;
258✔
7577

258✔
7578
  if (getcwd(cwdbuf, sizeof(cwdbuf)-1)) {
258✔
7579
    cwdbuf[sizeof(cwdbuf)-1] = '\0';
258✔
7580
    pr_fs_setcwd(cwdbuf);
258✔
7581

7582
  } else {
258✔
7583
    pr_fsio_chdir("/", FALSE);
7584
    pr_fs_setcwd("/");
7585
  }
7586

7587
  /* Prepare the stat cache as well. */
2✔
7588
  statcache_pool = make_sub_pool(permanent_pool);
2✔
7589
  pr_pool_tag(statcache_pool, "FS Statcache Pool");
7590
  stat_statcache_tab = pr_table_alloc(statcache_pool, 0);
2✔
7591
  stat_statcache_set = xaset_create(statcache_pool, NULL);
2✔
7592
  lstat_statcache_tab = pr_table_alloc(statcache_pool, 0);
7593
  lstat_statcache_set = xaset_create(statcache_pool, NULL);
7594

2✔
7595
  return 0;
2✔
7596
}
7597

7598
#ifdef PR_USE_DEVEL
2✔
7599

2✔
7600
static const char *get_fs_hooks_str(pool *p, pr_fs_t *fs) {
7601
  char *hooks = "";
7602

2✔
7603
  if (fs->stat) {
2✔
7604
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "stat(2)", NULL);
7605
  }
7606

2✔
7607
  if (fs->lstat) {
2✔
7608
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lstat(2)", NULL);
7609
  }
7610

2✔
7611
  if (fs->fstat) {
2✔
7612
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fstat(2)", NULL);
7613
  }
7614

2✔
7615
  if (fs->rename) {
2✔
7616
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "rename(2)", NULL);
7617
  }
7618

2✔
7619
  if (fs->link) {
2✔
7620
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "link(2)", NULL);
7621
  }
7622

2✔
7623
  if (fs->unlink) {
2✔
7624
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "unlink(2)", NULL);
7625
  }
7626

2✔
7627
  if (fs->open) {
2✔
7628
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "open(2)", NULL);
7629
  }
7630

2✔
7631
  if (fs->close) {
2✔
7632
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "close(2)", NULL);
7633
  }
7634

2✔
7635
  if (fs->read) {
2✔
7636
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "read(2)", NULL);
7637
  }
7638

2✔
7639
  if (fs->lseek) {
2✔
7640
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lseek(2)", NULL);
7641
  }
7642

2✔
7643
  if (fs->readlink) {
2✔
7644
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "readlink(2)", NULL);
7645
  }
7646

2✔
7647
  if (fs->symlink) {
2✔
7648
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "symlink(2)", NULL);
7649
  }
7650

2✔
7651
  if (fs->ftruncate) {
2✔
7652
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "ftruncate(2)", NULL);
7653
  }
7654

2✔
7655
  if (fs->truncate) {
2✔
7656
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "truncate(2)", NULL);
7657
  }
7658

2✔
7659
  if (fs->chmod) {
2✔
7660
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chmod(2)", NULL);
7661
  }
7662

2✔
7663
  if (fs->chown) {
2✔
7664
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chown(2)", NULL);
7665
  }
7666

2✔
7667
  if (fs->fchown) {
2✔
7668
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fchown(2)", NULL);
7669
  }
7670

2✔
7671
  if (fs->lchown) {
2✔
7672
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lchown(2)", NULL);
7673
  }
7674

2✔
7675
  if (fs->access) {
2✔
7676
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "access(2)", NULL);
7677
  }
7678

2✔
7679
  if (fs->faccess) {
2✔
7680
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "faccess(2)", NULL);
7681
  }
7682

2✔
7683
  if (fs->utimes) {
2✔
7684
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "utimes(2)", NULL);
7685
  }
7686

2✔
7687
  if (fs->futimes) {
2✔
7688
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "futimes(2)", NULL);
7689
  }
7690

2✔
7691
  if (fs->fsync) {
2✔
7692
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fsync(2)", NULL);
7693
  }
7694

2✔
7695
  if (fs->chdir) {
2✔
7696
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chdir(2)", NULL);
7697
  }
7698

2✔
7699
  if (fs->chroot) {
2✔
7700
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chroot(2)", NULL);
7701
  }
7702

2✔
7703
  if (fs->opendir) {
2✔
7704
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "opendir(3)", NULL);
7705
  }
7706

2✔
7707
  if (fs->closedir) {
×
7708
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "closedir(3)", NULL);
7709
  }
7710

7711
  if (fs->readdir) {
7712
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "readdir(3)", NULL);
7713
  }
2✔
7714

7715
  if (fs->mkdir) {
7716
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "mkdir(2)", NULL);
2✔
7717
  }
7718

2✔
7719
  if (!*hooks) {
2✔
7720
    return pstrdup(p, "(none)");
7721
  }
10✔
7722

10✔
7723
  return hooks;
10✔
7724
}
7725

10✔
7726
static void get_fs_info(pool *p, int depth, pr_fs_t *fs,
10✔
7727
    void (*dumpf)(const char *, ...)) {
10✔
7728

10✔
7729
  dumpf("FS#%u: '%s', mounted at '%s', implementing the following hooks:",
7730
    depth, fs->fs_name, fs->fs_path);
10✔
7731
  dumpf("FS#%u:    %s", depth, get_fs_hooks_str(p, fs));
10✔
7732
}
10✔
7733

7734
static void fs_printf(const char *fmt, ...) {
3✔
7735
  char buf[PR_TUNABLE_BUFFER_SIZE+1];
3✔
7736
  va_list msg;
7737

3✔
7738
  memset(buf, '\0', sizeof(buf));
3✔
7739
  va_start(msg, fmt);
7740
  pr_vsnprintf(buf, sizeof(buf)-1, fmt, msg);
7741
  va_end(msg);
3✔
7742

3✔
7743
  buf[sizeof(buf)-1] = '\0';
7744
  pr_trace_msg(trace_channel, 19, "%s", buf);
3✔
7745
}
2✔
7746

7747
void pr_fs_dump(void (*dumpf)(const char *, ...)) {
7748
  pool *p;
7749

2✔
7750
  if (dumpf == NULL) {
7751
    dumpf = fs_printf;
2✔
7752
  }
2✔
7753

2✔
7754
  dumpf("FS#0: 'system' mounted at '/', implementing the following hooks:");
7755
  dumpf("FS#0:    (all)");
4✔
7756

2✔
7757
  if (!fs_map ||
7758
      fs_map->nelts == 0) {
4✔
7759
    return;
2✔
7760
  }
7761

7762
  p = make_sub_pool(permanent_pool);
7763

7764
  if (fs_map->nelts > 0) {
2✔
7765
    pr_fs_t **fs_objs = (pr_fs_t **) fs_map->elts;
7766
    register unsigned int i;
7767

7768
    for (i = 0; i < fs_map->nelts; i++) {
7769
      pr_fs_t *fsi = fs_objs[i];
7770

7771
      for (; fsi->fs_next; fsi = fsi->fs_next) {
7772
        get_fs_info(p, i+1, fsi, dumpf);
7773
      }
7774
    }
7775
  }
7776

7777
  destroy_pool(p);
7778
}
7779
#endif /* PR_USE_DEVEL */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc