Coverage Report

Created: 2025-10-31 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proftpd/src/fsio.c
Line
Count
Source
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5
 * Copyright (c) 2001-2025 The ProFTPD Project
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20
 *
21
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22
 * and other respective copyright holders give permission to link this program
23
 * with OpenSSL, and distribute the resulting executable, without including
24
 * the source code for OpenSSL in the source distribution.
25
 */
26
27
/* ProFTPD virtual/modular file-system support */
28
29
#include "error.h"
30
#include "conf.h"
31
#include "privs.h"
32
33
#ifdef HAVE_SYS_STATVFS_H
34
# include <sys/statvfs.h>
35
#endif
36
37
#ifdef HAVE_SYS_VFS_H
38
# include <sys/vfs.h>
39
#endif
40
41
#ifdef HAVE_SYS_PARAM_H
42
# include <sys/param.h>
43
#endif
44
45
#ifdef HAVE_SYS_MOUNT_H
46
# include <sys/mount.h>
47
#endif
48
49
#ifdef AIX3
50
# include <sys/statfs.h>
51
#endif
52
53
#ifdef HAVE_ACL_LIBACL_H
54
# include <acl/libacl.h>
55
#endif
56
57
/* We will reset timers in the progress callback every Nth iteration of the
58
 * callback when copying a file.
59
 */
60
static size_t copy_iter_count = 0;
61
62
#ifndef COPY_PROGRESS_NTH_ITER
63
0
# define COPY_PROGRESS_NTH_ITER       50000
64
#endif
65
66
/* For determining whether a file is on an NFS filesystem.  Note that
67
 * this value is Linux specific.  See Bug#3874 for details.
68
 */
69
#ifndef NFS_SUPER_MAGIC
70
0
# define NFS_SUPER_MAGIC  0x6969
71
#endif
72
73
typedef struct fsopendir fsopendir_t;
74
75
struct fsopendir {
76
  fsopendir_t *next,*prev;
77
78
  /* pool for this object's use */
79
  pool *pool;
80
81
  pr_fs_t *fsdir;
82
  DIR *dir;
83
};
84
85
static pr_fs_t *root_fs = NULL, *fs_cwd = NULL;
86
static array_header *fs_map = NULL;
87
88
static fsopendir_t *fsopendir_list;
89
90
static void *fs_cache_dir = NULL;
91
static pr_fs_t *fs_cache_fsdir = NULL;
92
93
/* Internal flag set whenever a new pr_fs_t has been added or removed, and
94
 * cleared once the fs_map has been scanned
95
 */
96
static unsigned char chk_fs_map = FALSE;
97
98
/* Virtual working directory */
99
static char vwd[PR_TUNABLE_PATH_MAX + 1] = "/";
100
101
static char cwd[PR_TUNABLE_PATH_MAX + 1] = "/";
102
static size_t cwd_len = 1;
103
104
static int fsio_guard_chroot = FALSE;
105
static unsigned long fsio_opts = 0UL;
106
107
/* Runtime enabling/disabling of mkdtemp(3) use. */
108
#ifdef HAVE_MKDTEMP
109
static int fsio_use_mkdtemp = TRUE;
110
#else
111
static int fsio_use_mkdtemp = FALSE;
112
#endif /* HAVE_MKDTEMP */
113
114
/* Runtime enabling/disabling of encoding of paths. */
115
static int use_encoding = TRUE;
116
117
static const char *trace_channel = "fsio";
118
119
/* Guard against attacks like "Roaring Beast" when we are chrooted.  See:
120
 *
121
 *  https://auscert.org.au/15286
122
 *  https://auscert.org.au/15526
123
 *
124
 * Currently, we guard the /etc and /lib directories.
125
 */
126
0
static int chroot_allow_path(const char *path) {
127
0
  size_t path_len;
128
0
  int res = 0;
129
130
  /* Note: we expect to get (and DO get) the absolute path here.  Should that
131
   * ever not be the case, this check will not work.
132
   */
133
134
0
  path_len = strlen(path);
135
0
  if (path_len < 4) {
136
    /* Path is not long enough to include one of the guarded directories. */
137
0
    return 0;
138
0
  }
139
140
0
  if (path_len == 4) {
141
0
    if (strcmp(path, "/etc") == 0 ||
142
0
        strcmp(path, "/lib") == 0) {
143
0
      res = -1;
144
0
    }
145
146
0
  } else {
147
0
    if (strncmp(path, "/etc/", 5) == 0 ||
148
0
        strncmp(path, "/lib/", 5) == 0) {
149
0
      res = -1;
150
0
    }
151
0
  }
152
153
0
  if (res < 0) {
154
0
    pr_trace_msg(trace_channel, 1, "rejecting path '%s' within chroot '%s'",
155
0
      path, session.chroot_path);
156
0
    pr_log_debug(DEBUG2,
157
0
      "WARNING: attempt to use sensitive path '%s' within chroot '%s', "
158
0
      "rejecting", path, session.chroot_path);
159
160
0
    errno = EACCES;
161
0
  }
162
163
0
  return res;
164
0
}
165
166
/* Builtin/default "progress" callback for long-running file copies. */
167
0
static void copy_progress_cb(int nwritten) {
168
0
  int res;
169
170
0
  (void) nwritten;
171
172
0
  copy_iter_count++;
173
0
  if ((copy_iter_count % COPY_PROGRESS_NTH_ITER) != 0) {
174
0
    return;
175
0
  }
176
177
  /* Reset some of the Timeouts which might interfere, i.e. TimeoutIdle and
178
   * TimeoutNoDataTransfer.
179
   */
180
181
0
  res = pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);
182
0
  if (res < 0) {
183
0
    pr_trace_msg(trace_channel, 14, "error resetting TimeoutIdle timer: %s",
184
0
      strerror(errno));
185
0
  }
186
187
0
  res = pr_timer_reset(PR_TIMER_NOXFER, ANY_MODULE);
188
0
  if (res < 0) {
189
0
    pr_trace_msg(trace_channel, 14,
190
0
      "error resetting TimeoutNoTransfer timer: %s", strerror(errno));
191
0
  }
192
193
0
  res = pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
194
0
  if (res < 0) {
195
0
    pr_trace_msg(trace_channel, 14,
196
0
      "error resetting TimeoutStalled timer: %s", strerror(errno));
197
0
  }
198
0
}
199
200
/* The following static functions are simply wrappers for system functions
201
 */
202
203
0
static int sys_stat(pr_fs_t *fs, const char *path, struct stat *sbuf) {
204
0
  return stat(path, sbuf);
205
0
}
206
207
0
static int sys_fstat(pr_fh_t *fh, int fd, struct stat *sbuf) {
208
0
  return fstat(fd, sbuf);
209
0
}
210
211
0
static int sys_lstat(pr_fs_t *fs, const char *path, struct stat *sbuf) {
212
0
  return lstat(path, sbuf);
213
0
}
214
215
0
static int sys_rename(pr_fs_t *fs, const char *rnfm, const char *rnto) {
216
0
  int res;
217
218
0
  if (fsio_guard_chroot) {
219
0
    res = chroot_allow_path(rnfm);
220
0
    if (res < 0) {
221
0
      return -1;
222
0
    }
223
224
0
    res = chroot_allow_path(rnto);
225
0
    if (res < 0) {
226
0
      return -1;
227
0
    }
228
0
  }
229
230
0
  res = rename(rnfm, rnto);
231
0
  return res;
232
0
}
233
234
0
static int sys_unlink(pr_fs_t *fs, const char *path) {
235
0
  int res;
236
237
0
  if (fsio_guard_chroot) {
238
0
    res = chroot_allow_path(path);
239
0
    if (res < 0) {
240
0
      return -1;
241
0
    }
242
0
  }
243
244
0
  res = unlink(path);
245
0
  return res;
246
0
}
247
248
0
static int sys_open(pr_fh_t *fh, const char *path, int flags) {
249
0
  int res;
250
251
#ifdef O_BINARY
252
  /* On Cygwin systems, we need the open(2) equivalent of fopen(3)'s "b"
253
   * option.  Cygwin defines an O_BINARY flag for this purpose.
254
   */
255
  flags |= O_BINARY;
256
#endif
257
258
0
  if (fsio_guard_chroot) {
259
    /* If we are creating (or truncating) a file, then we need to check.
260
     * Note: should O_RDWR be added to this list?
261
     */
262
0
    if (flags & (O_APPEND|O_CREAT|O_TRUNC|O_WRONLY)) {
263
0
      res = chroot_allow_path(path);
264
0
      if (res < 0) {
265
0
        return -1;
266
0
      }
267
0
    }
268
0
  }
269
270
0
  res = open(path, flags, PR_OPEN_MODE);
271
0
  return res;
272
0
}
273
274
0
static int sys_close(pr_fh_t *fh, int fd) {
275
0
  return close(fd);
276
0
}
277
278
static ssize_t sys_pread(pr_fh_t *fh, int fd, void *buf, size_t sz,
279
0
    off_t offset) {
280
0
#if defined(HAVE_PREAD)
281
0
  return pread(fd, buf, sz, offset);
282
#else
283
  /* XXX Provide an implementation using lseek+read+lseek */
284
  errno = ENOSYS;
285
  return -1;
286
#endif /* HAVE_PREAD */
287
0
}
288
289
0
static int sys_read(pr_fh_t *fh, int fd, char *buf, size_t size) {
290
0
  return read(fd, buf, size);
291
0
}
292
293
static ssize_t sys_pwrite(pr_fh_t *fh, int fd, const void *buf, size_t sz,
294
0
    off_t offset) {
295
0
#if defined(HAVE_PWRITE)
296
0
  return pwrite(fd, buf, sz, offset);
297
#else
298
  /* XXX Provide an implementation using lseek+write+lseek */
299
  errno = ENOSYS;
300
  return -1;
301
#endif /* HAVE_PWRITE */
302
0
}
303
304
0
static int sys_write(pr_fh_t *fh, int fd, const char *buf, size_t size) {
305
0
  return write(fd, buf, size);
306
0
}
307
308
0
static off_t sys_lseek(pr_fh_t *fh, int fd, off_t offset, int whence) {
309
0
  return lseek(fd, offset, whence);
310
0
}
311
312
static int sys_link(pr_fs_t *fs, const char *target_path,
313
0
    const char *link_path) {
314
0
  int res;
315
316
0
  if (fsio_guard_chroot) {
317
0
    res = chroot_allow_path(link_path);
318
0
    if (res < 0) {
319
0
      return -1;
320
0
    }
321
0
  }
322
323
0
  res = link(target_path, link_path);
324
0
  return res;
325
0
}
326
327
static int sys_symlink(pr_fs_t *fs, const char *target_path,
328
0
    const char *link_path) {
329
0
  int res;
330
331
0
  if (fsio_guard_chroot) {
332
0
    res = chroot_allow_path(link_path);
333
0
    if (res < 0) {
334
0
      return -1;
335
0
    }
336
0
  }
337
338
0
  res = symlink(target_path, link_path);
339
0
  return res;
340
0
}
341
342
static int sys_readlink(pr_fs_t *fs, const char *path, char *buf,
343
0
    size_t buflen) {
344
0
  return readlink(path, buf, buflen);
345
0
}
346
347
0
static int sys_ftruncate(pr_fh_t *fh, int fd, off_t len) {
348
0
  return ftruncate(fd, len);
349
0
}
350
351
0
static int sys_truncate(pr_fs_t *fs, const char *path, off_t len) {
352
0
  int res;
353
354
0
  if (fsio_guard_chroot) {
355
0
    res = chroot_allow_path(path);
356
0
    if (res < 0) {
357
0
      return -1;
358
0
    }
359
0
  }
360
361
0
  res = truncate(path, len);
362
0
  return res;
363
0
}
364
365
0
static int sys_chmod(pr_fs_t *fs, const char *path, mode_t mode) {
366
0
  int res;
367
368
0
  if (fsio_guard_chroot) {
369
0
    res = chroot_allow_path(path);
370
0
    if (res < 0) {
371
0
      return -1;
372
0
    }
373
0
  }
374
375
0
  res = chmod(path, mode);
376
0
  return res;
377
0
}
378
379
0
static int sys_fchmod(pr_fh_t *fh, int fd, mode_t mode) {
380
0
  return fchmod(fd, mode);
381
0
}
382
383
0
static int sys_chown(pr_fs_t *fs, const char *path, uid_t uid, gid_t gid) {
384
0
  int res;
385
386
0
  if (fsio_guard_chroot) {
387
0
    res = chroot_allow_path(path);
388
0
    if (res < 0) {
389
0
      return -1;
390
0
    }
391
0
  }
392
393
0
  res = chown(path, uid, gid);
394
0
  return res;
395
0
}
396
397
0
static int sys_fchown(pr_fh_t *fh, int fd, uid_t uid, gid_t gid) {
398
0
  return fchown(fd, uid, gid);
399
0
}
400
401
0
static int sys_lchown(pr_fs_t *fs, const char *path, uid_t uid, gid_t gid) {
402
0
  int res;
403
404
0
  if (fsio_guard_chroot) {
405
0
    res = chroot_allow_path(path);
406
0
    if (res < 0) {
407
0
      return -1;
408
0
    }
409
0
  }
410
411
0
  res = lchown(path, uid, gid);
412
0
  return res;
413
0
}
414
415
/* We provide our own equivalent of access(2) here, rather than using
416
 * access(2) directly, because access(2) uses the real IDs, rather than
417
 * the effective IDs, of the process.
418
 */
419
static int sys_access(pr_fs_t *fs, const char *path, int mode, uid_t uid,
420
0
    gid_t gid, array_header *suppl_gids) {
421
0
  struct stat st;
422
423
0
  if (pr_fsio_stat(path, &st) < 0) {
424
0
    return -1;
425
0
  }
426
427
0
  return pr_fs_have_access(&st, mode, uid, gid, suppl_gids);
428
0
}
429
430
static int sys_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
431
0
    array_header *suppl_gids) {
432
0
  return sys_access(fh->fh_fs, fh->fh_path, mode, uid, gid, suppl_gids);
433
0
}
434
435
0
static int sys_utimes(pr_fs_t *fs, const char *path, struct timeval *tvs) {
436
0
  int res;
437
438
0
  if (fsio_guard_chroot) {
439
0
    res = chroot_allow_path(path);
440
0
    if (res < 0) {
441
0
      return -1;
442
0
    }
443
0
  }
444
445
0
  res = utimes(path, tvs);
446
0
  return res;
447
0
}
448
449
0
static int sys_futimes(pr_fh_t *fh, int fd, struct timeval *tvs) {
450
0
#ifdef HAVE_FUTIMES
451
0
  int res;
452
453
  /* Check for an ENOSYS errno; if so, fallback to using sys_utimes.  Some
454
   * platforms will provide a futimes(2) stub which does not actually do
455
   * anything.
456
   */
457
0
  res = futimes(fd, tvs);
458
0
  if (res < 0 &&
459
0
      errno == ENOSYS) {
460
0
    return sys_utimes(fh->fh_fs, fh->fh_path, tvs);
461
0
  }
462
463
0
  return res;
464
#else
465
  return sys_utimes(fh->fh_fs, fh->fh_path, tvs);
466
#endif
467
0
}
468
469
0
static int sys_fsync(pr_fh_t *fh, int fd) {
470
0
  int res;
471
472
0
#ifdef HAVE_FSYNC
473
0
  res = fsync(fd);
474
#else
475
  errno = ENOSYS;
476
  res = -1;
477
#endif /* HAVE_FSYNC */
478
479
0
  return res;
480
0
}
481
482
0
static const char *sys_realpath(pr_fs_t *fs, pool *p, const char *path) {
483
0
  (void) fs;
484
485
  /* The default implementation does NOT use the realpath(3) function, and
486
   * instead is a pass-through.
487
   */
488
0
  return pstrdup(p, path);
489
0
}
490
491
static ssize_t sys_getxattr(pool *p, pr_fs_t *fs, const char *path,
492
0
    const char *name, void *val, size_t valsz) {
493
0
  ssize_t res = -1;
494
495
0
  (void) p;
496
497
0
#if defined(PR_USE_XATTR)
498
# if defined(HAVE_SYS_EXTATTR_H)
499
  res = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
500
# elif defined(HAVE_SYS_XATTR_H)
501
#  if defined(XATTR_NOFOLLOW)
502
  res = getxattr(path, name, val, valsz, 0, 0);
503
#  else
504
0
  res = getxattr(path, name, val, valsz);
505
0
#  endif /* XATTR_NOFOLLOW */
506
# else
507
  errno = NOSYS;
508
  res = -1;
509
# endif /* HAVE_SYS_XATTR_H */
510
#else
511
  (void) fs;
512
  (void) path;
513
  (void) name;
514
  (void) val;
515
  (void) valsz;
516
517
  errno = ENOSYS;
518
  res = -1;
519
#endif /* PR_USE_XATTR */
520
521
0
  return res;
522
0
}
523
524
static ssize_t sys_lgetxattr(pool *p, pr_fs_t *fs, const char *path,
525
0
    const char *name, void *val, size_t valsz) {
526
0
  ssize_t res = -1;
527
528
0
  (void) p;
529
530
0
#if defined(PR_USE_XATTR)
531
# if defined(HAVE_SYS_EXTATTR_H)
532
#  if defined(HAVE_EXTATTR_GET_LINK)
533
  res = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
534
#  else
535
  res = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
536
#  endif /* HAVE_EXTATTR_GET_LINK */
537
# elif defined(HAVE_SYS_XATTR_H)
538
#  if defined(HAVE_LGETXATTR)
539
0
  res = lgetxattr(path, name, val, valsz);
540
#  elif defined(XATTR_NOFOLLOW)
541
  res = getxattr(path, name, val, valsz, 0, XATTR_NOFOLLOW);
542
#  else
543
  res = getxattr(path, name, val, valsz);
544
#  endif /* HAVE_LGETXATTR */
545
# else
546
  errno = ENOSYS;
547
  res = -1;
548
# endif /* HAVE_SYS_XATTR_H */
549
#else
550
  (void) fs;
551
  (void) path;
552
  (void) name;
553
  (void) val;
554
  (void) valsz;
555
556
  errno = ENOSYS;
557
  res = -1;
558
#endif /* PR_USE_XATTR */
559
560
0
  return res;
561
0
}
562
563
static ssize_t sys_fgetxattr(pool *p, pr_fh_t *fh, int fd, const char *name,
564
0
    void *val, size_t valsz) {
565
0
  ssize_t res = -1;
566
567
0
  (void) p;
568
569
0
#if defined(PR_USE_XATTR)
570
# if defined(HAVE_SYS_EXTATTR_H)
571
  res = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name, val, valsz);
572
# elif defined(HAVE_SYS_XATTR_H)
573
#  if defined(XATTR_NOFOLLOW)
574
  res = fgetxattr(fd, name, val, valsz, 0, 0);
575
#  else
576
0
  res = fgetxattr(fd, name, val, valsz);
577
0
#  endif /* XATTR_NOFOLLOW */
578
# else
579
  errno = ENOSYS;
580
  res = -1;
581
# endif /* HAVE_SYS_XATTR_H */
582
#else
583
  (void) fh;
584
  (void) fd;
585
  (void) name;
586
  (void) val;
587
  (void) valsz;
588
589
  errno = ENOSYS;
590
  res = -1;
591
#endif /* PR_USE_XATTR */
592
593
0
  return res;
594
0
}
595
596
#if defined(PR_USE_XATTR)
597
0
static array_header *parse_xattr_namelist(pool *p, char *namelist, size_t sz) {
598
0
  array_header *names;
599
0
  char *ptr;
600
601
0
  names = make_array(p, 0, sizeof(char *));
602
0
  ptr = namelist;
603
604
# if defined(HAVE_SYS_EXTATTR_H)
605
  /* BSD style name lists use a one-byte length prefix (limiting xattr names
606
   * to a maximum length of 255 bytes), followed by the name, without any
607
   * terminating NUL.
608
   */
609
  while (sz > 0) {
610
    unsigned char len;
611
612
    pr_signals_handle();
613
614
    len = (unsigned char) *ptr;
615
    ptr++;
616
    sz--;
617
618
    *((char **) push_array(names)) = pstrndup(p, ptr, len);
619
620
    ptr += len;
621
    sz -= len;
622
  }
623
624
# elif defined(HAVE_SYS_XATTR_H)
625
  /* Linux/MacOSX style name lists use NUL-terminated xattr names. */
626
0
  while (sz > 0) {
627
0
    char *ptr2;
628
0
    size_t len;
629
630
0
    pr_signals_handle();
631
632
0
    for (ptr2 = ptr; *ptr2; ptr2++) {
633
0
    }
634
0
    len = ptr2 - ptr;
635
0
    *((char **) push_array(names)) = pstrndup(p, ptr, len);
636
637
0
    ptr = ptr2 + 1;
638
0
    sz -= (len + 1);
639
0
  }
640
0
# endif /* HAVE_SYS_XATTR_H */
641
642
0
  return names;
643
0
}
644
645
0
static ssize_t unix_listxattr(const char *path, char *namelist, size_t len) {
646
0
  ssize_t res = -1;
647
648
#if defined(HAVE_SYS_EXTATTR_H)
649
  res = extattr_list_file(path, EXTATTR_NAMESPACE_USER, namelist, len);
650
#elif defined(HAVE_SYS_XATTR_H)
651
# if defined(XATTR_NOFOLLOW)
652
  res = listxattr(path, namelist, len, 0);
653
# else
654
0
  res = listxattr(path, namelist, len);
655
0
# endif /* XATTR_NOFOLLOW */
656
#else
657
  errno = ENOSYS;
658
  res = -1;
659
#endif /* HAVE_SYS_XATTR_H */
660
661
0
  return res;
662
0
}
663
664
0
static ssize_t unix_llistxattr(const char *path, char *namelist, size_t len) {
665
0
  ssize_t res = -1;
666
667
# if defined(HAVE_SYS_EXTATTR_H)
668
#  if defined(HAVE_EXTATTR_LIST_LINK)
669
  res = extattr_list_link(path, EXTATTR_NAMESPACE_USER, namelist, len);
670
#  else
671
  res = extattr_list_file(path, EXTATTR_NAMESPACE_USER, namelist, len);
672
#  endif /* HAVE_EXTATTR_LIST_LINK */
673
# elif defined(HAVE_SYS_XATTR_H)
674
#  if defined(HAVE_LLISTXATTR)
675
  res = llistxattr(path, namelist, len);
676
#  elif defined(XATTR_NOFOLLOW)
677
  res = listxattr(path, namelist, len, XATTR_NOFOLLOW);
678
#  else
679
0
  res = listxattr(path, namelist, len);
680
0
#  endif /* XATTR_NOFOLLOW */
681
# else
682
  errno = ENOSYS;
683
  res = -1;
684
# endif /* HAVE_SYS_XATTR_H */
685
686
0
  return res;
687
0
}
688
689
0
static ssize_t unix_flistxattr(int fd, char *namelist, size_t len) {
690
0
  ssize_t res = -1;
691
692
# if defined(HAVE_SYS_EXTATTR_H)
693
  res = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, namelist, len);
694
# elif defined(HAVE_SYS_XATTR_H)
695
#  if defined(XATTR_NOFOLLOW)
696
  res = flistxattr(fd, namelist, len, 0);
697
#  else
698
0
  res = flistxattr(fd, namelist, len);
699
0
#  endif /* XATTR_NOFOLLOW */
700
# else
701
  errno = ENOSYS;
702
  res = -1;
703
# endif /* HAVE_SYS_XATTR_H */
704
705
0
  return res;
706
0
}
707
#endif /* PR_USE_XATTR */
708
709
static int sys_listxattr(pool *p, pr_fs_t *fs, const char *path,
710
0
    array_header **names) {
711
0
  ssize_t res = 0;
712
0
  char *namelist = NULL;
713
0
  size_t len = 0;
714
715
0
#if defined(PR_USE_XATTR)
716
  /* We need to handle the different formats of namelists that listxattr et al
717
   * can provide.  On *BSDs, the namelist buffer uses length prefixes and no
718
   * terminating NULs; on Linux/Mac, the namelist buffer uses ONLY
719
   * NUL-terminated names.
720
   *
721
   * Thus we ALWAYS provide all the available attribute names, by first
722
   * querying for the full namelist buffer size, allocating that out of
723
   * given pool, querying for the names (using the buffer), and then parsing
724
   * them into an array.
725
   */
726
727
0
  res = unix_listxattr(path, NULL, 0);
728
0
  if (res < 0) {
729
0
    return -1;
730
0
  }
731
732
0
  if (res == 0) {
733
    /* No extended attributes found. */
734
0
    pr_trace_msg(trace_channel, 15, "listxattr: found 0 xattr names for '%s'",
735
0
      path);
736
0
    return 0;
737
0
  }
738
739
0
  len = res;
740
0
  namelist = palloc(p, len);
741
742
0
  res = unix_listxattr(path, namelist, len);
743
0
  if (res < 0) {
744
0
    return -1;
745
0
  }
746
747
0
  if (res == 0) {
748
    /* No extended attributes found. */
749
0
    pr_trace_msg(trace_channel, 15, "listxattr: found 0 xattr names for '%s'",
750
0
      path);
751
0
    return 0;
752
0
  }
753
754
0
  *names = parse_xattr_namelist(p, namelist, len);
755
0
  if (pr_trace_get_level(trace_channel) >= 15) {
756
0
    register unsigned int i;
757
0
    unsigned int count;
758
0
    const char **attr_names;
759
760
0
    count = (*names)->nelts;
761
0
    attr_names = (*names)->elts;
762
763
0
    pr_trace_msg(trace_channel, 15, "listxattr: found %d xattr names for '%s'",
764
0
      count, path);
765
0
    for (i = 0; i < count; i++) {
766
0
      pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]);
767
0
    }
768
0
  }
769
770
0
  res = (*names)->nelts;
771
772
#else
773
  (void) fs;
774
  (void) path;
775
  (void) names;
776
  (void) namelist;
777
  (void) len;
778
  errno = ENOSYS;
779
  res = -1;
780
#endif /* PR_USE_XATTR */
781
782
0
  return (int) res;
783
0
}
784
785
static int sys_llistxattr(pool *p, pr_fs_t *fs, const char *path,
786
0
    array_header **names) {
787
0
  ssize_t res;
788
0
  char *namelist = NULL;
789
0
  size_t len = 0;
790
791
0
#if defined(PR_USE_XATTR)
792
  /* See sys_listxattr for a description of why we use this approach. */
793
0
  res = unix_llistxattr(path, NULL, 0);
794
0
  if (res < 0) {
795
0
    return -1;
796
0
  }
797
798
0
  if (res == 0) {
799
    /* No extended attributes found. */
800
0
    pr_trace_msg(trace_channel, 15, "llistxattr: found 0 xattr names for '%s'",
801
0
      path);
802
0
    return 0;
803
0
  }
804
805
0
  len = res;
806
0
  namelist = palloc(p, len);
807
808
0
  res = unix_llistxattr(path, namelist, len);
809
0
  if (res < 0) {
810
0
    return -1;
811
0
  }
812
813
0
  if (res == 0) {
814
    /* No extended attributes found. */
815
0
    pr_trace_msg(trace_channel, 15, "llistxattr: found 0 xattr names for '%s'",
816
0
      path);
817
0
    return 0;
818
0
  }
819
820
0
  *names = parse_xattr_namelist(p, namelist, len);
821
0
  if (pr_trace_get_level(trace_channel) >= 15) {
822
0
    register unsigned int i;
823
0
    unsigned int count;
824
0
    const char **attr_names;
825
826
0
    count = (*names)->nelts;
827
0
    attr_names = (*names)->elts;
828
829
0
    pr_trace_msg(trace_channel, 15, "llistxattr: found %d xattr names for '%s'",
830
0
      count, path);
831
0
    for (i = 0; i < count; i++) {
832
0
      pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]);
833
0
    }
834
0
  }
835
836
0
  res = (*names)->nelts;
837
838
#else
839
  (void) fs;
840
  (void) path;
841
  (void) names;
842
  (void) namelist;
843
  (void) len;
844
  errno = ENOSYS;
845
  res = -1;
846
#endif /* PR_USE_XATTR */
847
848
0
  return (int) res;
849
0
}
850
851
0
static int sys_flistxattr(pool *p, pr_fh_t *fh, int fd, array_header **names) {
852
0
  ssize_t res;
853
0
  char *namelist = NULL;
854
0
  size_t len = 0;
855
856
0
#if defined(PR_USE_XATTR)
857
  /* See sys_listxattr for a description of why we use this approach. */
858
0
  res = unix_flistxattr(fd, NULL, 0);
859
0
  if (res < 0) {
860
0
    return -1;
861
0
  }
862
863
0
  if (res == 0) {
864
    /* No extended attributes found. */
865
0
    pr_trace_msg(trace_channel, 15, "flistxattr: found 0 xattr names for '%s'",
866
0
      fh->fh_path);
867
0
    return 0;
868
0
  }
869
870
0
  len = res;
871
0
  namelist = palloc(p, len);
872
873
0
  res = unix_flistxattr(fd, namelist, len);
874
0
  if (res < 0) {
875
0
    return -1;
876
0
  }
877
878
0
  if (res == 0) {
879
    /* No extended attributes found. */
880
0
    pr_trace_msg(trace_channel, 15, "flistxattr: found 0 xattr names for '%s'",
881
0
      fh->fh_path);
882
0
    return 0;
883
0
  }
884
885
0
  *names = parse_xattr_namelist(p, namelist, len);
886
0
  if (pr_trace_get_level(trace_channel) >= 15) {
887
0
    register unsigned int i;
888
0
    unsigned int count;
889
0
    const char **attr_names;
890
891
0
    count = (*names)->nelts;
892
0
    attr_names = (*names)->elts;
893
894
0
    pr_trace_msg(trace_channel, 15, "flistxattr: found %d xattr names for '%s'",
895
0
      count, fh->fh_path);
896
0
    for (i = 0; i < count; i++) {
897
0
      pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]);
898
0
    }
899
0
  }
900
901
0
  res = (*names)->nelts;
902
903
#else
904
  (void) fh;
905
  (void) fd;
906
  (void) names;
907
  (void) namelist;
908
  (void) len;
909
  errno = ENOSYS;
910
  res = -1;
911
#endif /* PR_USE_XATTR */
912
913
0
  return (int) res;
914
0
}
915
916
static int sys_removexattr(pool *p, pr_fs_t *fs, const char *path,
917
0
    const char *name) {
918
0
  int res = -1;
919
920
0
  (void) p;
921
922
0
#if defined(PR_USE_XATTR)
923
# if defined(HAVE_SYS_EXTATTR_H)
924
  res = extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name);
925
# elif defined(HAVE_SYS_XATTR_H)
926
#  if defined(XATTR_NOFOLLOW)
927
  res = removexattr(path, name, 0);
928
#  else
929
0
  res = removexattr(path, name);
930
0
#  endif /* XATTR_NOFOLLOW */
931
# else
932
  errno = ENOSYS;
933
  res = -1;
934
# endif /* HAVE_SYS_XATTR_H */
935
#else
936
  (void) fs;
937
  (void) path;
938
  (void) name;
939
940
  errno = ENOSYS;
941
  res = -1;
942
#endif /* PR_USE_XATTR */
943
944
0
  return res;
945
0
}
946
947
static int sys_lremovexattr(pool *p, pr_fs_t *fs, const char *path,
948
0
    const char *name) {
949
0
  int res;
950
951
0
  (void) p;
952
953
0
#if defined(PR_USE_XATTR)
954
# if defined(HAVE_SYS_EXTATTR_H)
955
#  if defined(HAVE_EXTATTR_DELETE_LINK)
956
  res = extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
957
#  else
958
  res = extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name);
959
#  endif /* HAVE_EXTATTR_DELETE_LINK */
960
# elif defined(HAVE_SYS_XATTR_H)
961
#  if defined(HAVE_LREMOVEXATTR)
962
0
  res = lremovexattr(path, name);
963
#  elif defined(XATTR_NOFOLLOW)
964
  res = removexattr(path, name, XATTR_NOFOLLOW);
965
#  else
966
  res = removexattr(path, name);
967
#  endif /* XATTR_NOFOLLOW */
968
# else
969
  errno = ENOSYS;
970
  res = -1;
971
# endif /* HAVE_SYS_XATTR_H */
972
#else
973
  (void) fs;
974
  (void) path;
975
  (void) name;
976
977
  errno = ENOSYS;
978
  res = -1;
979
#endif /* PR_USE_XATTR */
980
981
0
  return res;
982
0
}
983
984
0
static int sys_fremovexattr(pool *p, pr_fh_t *fh, int fd, const char *name) {
985
0
  int res;
986
987
0
  (void) p;
988
989
0
#if defined(PR_USE_XATTR)
990
# if defined(HAVE_SYS_EXTATTR_H)
991
  res = extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, name);
992
# elif defined(HAVE_SYS_XATTR_H)
993
#  if defined(XATTR_NOFOLLOW)
994
  res = fremovexattr(fd, name, 0);
995
#  else
996
0
  res = fremovexattr(fd, name);
997
0
#  endif /* XATTR_NOFOLLOW */
998
# else
999
  errno = ENOSYS;
1000
  res = -1;
1001
# endif /* HAVE_SYS_XATTR_H */
1002
#else
1003
  (void) fh;
1004
  (void) fd;
1005
  (void) name;
1006
1007
  errno = ENOSYS;
1008
  res = -1;
1009
#endif /* PR_USE_XATTR */
1010
1011
0
  return res;
1012
0
}
1013
1014
#if defined(PR_USE_XATTR) && defined(HAVE_SYS_XATTR_H)
1015
/* Map the given flags onto the sys/xattr.h flags */
1016
0
static int get_setxattr_flags(int fsio_flags) {
1017
0
  int xattr_flags = 0;
1018
1019
  /* If both CREATE and REPLACE are set, use a value of zero; per the
1020
   * man pages, this value gives the desired "create or replace" semantics.
1021
   * Right?
1022
   */
1023
1024
0
  if (fsio_flags & PR_FSIO_XATTR_FL_CREATE) {
1025
0
#if defined(XATTR_CREATE)
1026
0
    xattr_flags = XATTR_CREATE;
1027
0
#endif /* XATTR_CREATE */
1028
1029
0
    if (fsio_flags & PR_FSIO_XATTR_FL_REPLACE) {
1030
0
      xattr_flags = 0;
1031
0
    }
1032
1033
0
  } else if (fsio_flags & PR_FSIO_XATTR_FL_REPLACE) {
1034
0
#if defined(XATTR_REPLACE)
1035
0
    xattr_flags = XATTR_REPLACE;
1036
0
#endif /* XATTR_REPLACE */
1037
0
  }
1038
1039
0
  return xattr_flags;
1040
0
}
1041
#endif /* PR_USE_XATTR and <sys/xattr.h> */
1042
1043
static int sys_setxattr(pool *p, pr_fs_t *fs, const char *path,
1044
0
    const char *name, void *val, size_t valsz, int flags) {
1045
0
  int res, xattr_flags = 0;
1046
1047
0
  (void) p;
1048
1049
0
#if defined(PR_USE_XATTR)
1050
# if defined(HAVE_SYS_EXTATTR_H)
1051
  (void) xattr_flags;
1052
  res = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
1053
1054
# elif defined(HAVE_SYS_XATTR_H)
1055
  xattr_flags = get_setxattr_flags(flags);
1056
1057
#  if defined(XATTR_NOFOLLOW)
1058
  res = setxattr(path, name, val, valsz, 0, xattr_flags);
1059
#  else
1060
0
  res = setxattr(path, name, val, valsz, xattr_flags);
1061
0
#  endif /* XATTR_NOFOLLOW */
1062
# else
1063
  errno = NOSYS;
1064
  res = -1;
1065
# endif /* HAVE_SYS_XATTR_H */
1066
#else
1067
  (void) fs;
1068
  (void) path;
1069
  (void) name;
1070
  (void) val;
1071
  (void) valsz;
1072
  (void) flags;
1073
  (void) xattr_flags;
1074
1075
  errno = ENOSYS;
1076
  res = -1;
1077
#endif /* PR_USE_XATTR */
1078
1079
0
  return res;
1080
0
}
1081
1082
static int sys_lsetxattr(pool *p, pr_fs_t *fs, const char *path,
1083
0
    const char *name, void *val, size_t valsz, int flags) {
1084
0
  int res, xattr_flags = 0;
1085
1086
0
  (void) p;
1087
1088
0
#if defined(PR_USE_XATTR)
1089
# if defined(HAVE_SYS_EXTATTR_H)
1090
  (void) xattr_flags;
1091
#  if defined(HAVE_EXTATTR_SET_LINK)
1092
  res = extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
1093
#  else
1094
  res = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz);
1095
#  endif /* HAVE_EXTATTR_SET_LINK */
1096
# elif defined(HAVE_SYS_XATTR_H)
1097
  xattr_flags = get_setxattr_flags(flags);
1098
1099
#  if defined(HAVE_LSETXATTR)
1100
  res = lsetxattr(path, name, val, valsz, xattr_flags);
1101
#  elif defined(XATTR_NOFOLLOW)
1102
  xattr_flags |= XATTR_NOFOLLOW;
1103
  res = setxattr(path, name, val, valsz, 0, xattr_flags);
1104
#  else
1105
0
  res = setxattr(path, name, val, valsz, xattr_flags);
1106
0
#  endif /* XATTR_NOFOLLOW */
1107
# else
1108
  errno = ENOSYS;
1109
  res = -1;
1110
# endif /* HAVE_SYS_XATTR_H */
1111
#else
1112
  (void) fs;
1113
  (void) path;
1114
  (void) name;
1115
  (void) val;
1116
  (void) valsz;
1117
  (void) flags;
1118
  (void) xattr_flags;
1119
1120
  errno = ENOSYS;
1121
  res = -1;
1122
#endif /* PR_USE_XATTR */
1123
1124
0
  return res;
1125
0
}
1126
1127
static int sys_fsetxattr(pool *p, pr_fh_t *fh, int fd, const char *name,
1128
0
    void *val, size_t valsz, int flags) {
1129
0
  int res, xattr_flags = 0;
1130
1131
0
  (void) p;
1132
1133
0
#if defined(PR_USE_XATTR)
1134
# if defined(HAVE_SYS_EXTATTR_H)
1135
  (void) xattr_flags;
1136
  res = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, name, val, valsz);
1137
1138
# elif defined(HAVE_SYS_XATTR_H)
1139
  xattr_flags = get_setxattr_flags(flags);
1140
1141
#  if defined(XATTR_NOFOLLOW)
1142
  res = fsetxattr(fd, name, val, valsz, 0, xattr_flags);
1143
#  else
1144
0
  res = fsetxattr(fd, name, val, valsz, xattr_flags);
1145
0
#  endif /* XATTR_NOFOLLOW */
1146
# else
1147
  errno = ENOSYS;
1148
  res = -1;
1149
# endif /* HAVE_SYS_XATTR_H */
1150
#else
1151
  (void) fh;
1152
  (void) fd;
1153
  (void) name;
1154
  (void) val;
1155
  (void) valsz;
1156
  (void) flags;
1157
  (void) xattr_flags;
1158
1159
  errno = ENOSYS;
1160
  res = -1;
1161
#endif /* PR_USE_XATTR */
1162
1163
0
  return res;
1164
0
}
1165
1166
0
static int sys_chroot(pr_fs_t *fs, const char *path) {
1167
0
  if (chroot(path) < 0) {
1168
0
    return -1;
1169
0
  }
1170
1171
0
  session.chroot_path = (char *) path;
1172
0
  return 0;
1173
0
}
1174
1175
0
static int sys_chdir(pr_fs_t *fs, const char *path) {
1176
0
  if (chdir(path) < 0) {
1177
0
    return -1;
1178
0
  }
1179
1180
0
  pr_fs_setcwd(path);
1181
0
  return 0;
1182
0
}
1183
1184
0
static void *sys_opendir(pr_fs_t *fs, const char *path) {
1185
0
  return opendir(path);
1186
0
}
1187
1188
0
static int sys_closedir(pr_fs_t *fs, void *dir) {
1189
0
  return closedir((DIR *) dir);
1190
0
}
1191
1192
0
static struct dirent *sys_readdir(pr_fs_t *fs, void *dir) {
1193
0
  return readdir((DIR *) dir);
1194
0
}
1195
1196
0
static int sys_mkdir(pr_fs_t *fs, const char *path, mode_t mode) {
1197
0
  int res;
1198
1199
0
  if (fsio_guard_chroot) {
1200
0
    res = chroot_allow_path(path);
1201
0
    if (res < 0) {
1202
0
      return -1;
1203
0
    }
1204
0
  }
1205
1206
0
  res = mkdir(path, mode);
1207
0
  return res;
1208
0
}
1209
1210
0
static int sys_rmdir(pr_fs_t *fs, const char *path) {
1211
0
  int res;
1212
1213
0
  if (fsio_guard_chroot) {
1214
0
    res = chroot_allow_path(path);
1215
0
    if (res < 0) {
1216
0
      return -1;
1217
0
    }
1218
0
  }
1219
1220
0
  res = rmdir(path);
1221
0
  return res;
1222
0
}
1223
1224
0
static int fs_cmp(const void *a, const void *b) {
1225
0
  pr_fs_t *fsa, *fsb;
1226
1227
0
  if (a == NULL) {
1228
0
    if (b == NULL) {
1229
0
      return 0;
1230
0
    }
1231
1232
0
    return 1;
1233
0
  }
1234
1235
0
  if (b == NULL) {
1236
0
    return -1;
1237
0
  }
1238
1239
0
  fsa = *((pr_fs_t **) a);
1240
0
  fsb = *((pr_fs_t **) b);
1241
1242
0
  return strcmp(fsa->fs_path, fsb->fs_path);
1243
0
}
1244
1245
/* Statcache stuff */
1246
struct fs_statcache {
1247
  xasetmember_t *next, *prev;
1248
  pool *sc_pool;
1249
  const char *sc_path;
1250
  struct stat sc_stat;
1251
  int sc_errno;
1252
  int sc_retval;
1253
  time_t sc_cached_ts;
1254
};
1255
1256
static const char *statcache_channel = "fs.statcache";
1257
static pool *statcache_pool = NULL;
1258
static unsigned int statcache_size = 0;
1259
static unsigned int statcache_max_age = 0;
1260
static unsigned int statcache_flags = 0;
1261
1262
/* We need to maintain two different caches: one for stat(2) data, and one
1263
 * for lstat(2) data.  For some files (e.g. symlinks), the struct stat data
1264
 * for the same path will be different for the two system calls.
1265
 */
1266
static pr_table_t *stat_statcache_tab = NULL;
1267
static xaset_t *stat_statcache_set = NULL;
1268
static pr_table_t *lstat_statcache_tab = NULL;
1269
static xaset_t *lstat_statcache_set = NULL;
1270
1271
0
#define fs_cache_lstat(f, p, s) cache_stat((f), (p), (s), FSIO_FILE_LSTAT)
1272
0
#define fs_cache_stat(f, p, s) cache_stat((f), (p), (s), FSIO_FILE_STAT)
1273
1274
static const struct fs_statcache *fs_statcache_get(pr_table_t *cache_tab,
1275
0
    xaset_t *cache_set, const char *path, size_t path_len, time_t now) {
1276
0
  const struct fs_statcache *sc = NULL;
1277
1278
0
  if (pr_table_count(cache_tab) == 0) {
1279
0
    errno = EPERM;
1280
0
    return NULL;
1281
0
  }
1282
1283
0
  sc = pr_table_get(cache_tab, path, NULL);
1284
0
  if (sc != NULL) {
1285
0
    time_t age;
1286
1287
    /* If this item hasn't expired yet, return it, otherwise, remove it. */
1288
0
    age = now - sc->sc_cached_ts;
1289
0
    if (age <= statcache_max_age) {
1290
0
      pr_trace_msg(statcache_channel, 19,
1291
0
        "using cached entry for '%s' (age %lu %s)", path,
1292
0
        (unsigned long) age, age != 1 ? "secs" : "sec");
1293
0
      return sc;
1294
0
    }
1295
1296
0
    pr_trace_msg(statcache_channel, 14,
1297
0
      "entry for '%s' expired (age %lu %s > max age %lu), removing", path,
1298
0
      (unsigned long) age, age != 1 ? "secs" : "sec",
1299
0
      (unsigned long) statcache_max_age);
1300
0
    (void) pr_table_remove(cache_tab, path, NULL);
1301
0
    (void) xaset_remove(cache_set, (xasetmember_t *) sc);
1302
0
    destroy_pool(sc->sc_pool);
1303
0
  }
1304
1305
0
  errno = ENOENT;
1306
0
  return NULL;
1307
0
}
1308
1309
static int fs_statcache_evict(pr_table_t *cache_tab, xaset_t *cache_set,
1310
0
    time_t now) {
1311
0
  time_t age;
1312
0
  xasetmember_t *item;
1313
0
  struct fs_statcache *sc;
1314
1315
0
  if (cache_set->xas_list == NULL) {
1316
    /* Should never happen. */
1317
0
    errno = EPERM;
1318
0
    return -1;
1319
0
  }
1320
1321
  /* We only need to remove the FIRST expired item; it should be the first
1322
   * item in the list, since we keep the list in insert (and thus expiry)
1323
   * order.
1324
   */
1325
1326
0
  item = cache_set->xas_list;
1327
0
  sc = (struct fs_statcache *) item;
1328
0
  age = now - sc->sc_cached_ts;
1329
1330
0
  if (age < statcache_max_age) {
1331
0
    errno = ENOENT;
1332
0
    return -1;
1333
0
  }
1334
1335
0
  pr_trace_msg(statcache_channel, 14,
1336
0
    "entry for '%s' expired (age %lu %s > max age %lu), evicting",
1337
0
    sc->sc_path, (unsigned long) age, age != 1 ? "secs" : "sec",
1338
0
    (unsigned long) statcache_max_age);
1339
1340
0
  (void) pr_table_remove(cache_tab, sc->sc_path, NULL);
1341
0
  (void) xaset_remove(cache_set, (xasetmember_t *) sc);
1342
0
  destroy_pool(sc->sc_pool);
1343
0
  return 0;
1344
0
}
1345
1346
/* Returns 1 if we successfully added a cache entry, 0 if not, and -1 if
1347
 * there was an error.
1348
 */
1349
static int fs_statcache_add(pr_table_t *cache_tab, xaset_t *cache_set,
1350
    const char *path, size_t path_len, struct stat *st, int xerrno,
1351
0
    int retval, time_t now) {
1352
0
  int res, table_count;
1353
0
  pool *sc_pool;
1354
0
  struct fs_statcache *sc;
1355
1356
0
  if (statcache_size == 0 ||
1357
0
      statcache_max_age == 0) {
1358
    /* Caching disabled; nothing to do here. */
1359
0
    return 0;
1360
0
  }
1361
1362
0
  table_count = pr_table_count(cache_tab);
1363
0
  if (table_count > 0 &&
1364
0
      (unsigned int) table_count >= statcache_size) {
1365
    /* We've reached capacity, and need to evict an item to make room. */
1366
0
    if (fs_statcache_evict(cache_tab, cache_set, now) < 0) {
1367
0
      pr_trace_msg(statcache_channel, 8,
1368
0
        "unable to evict enough items from the cache: %s", strerror(errno));
1369
0
    }
1370
1371
    /* We did not evict any items, and so are at capacity. */
1372
0
    return 0;
1373
0
  }
1374
1375
0
  sc_pool = make_sub_pool(statcache_pool);
1376
0
  pr_pool_tag(sc_pool, "FS statcache entry pool");
1377
0
  sc = pcalloc(sc_pool, sizeof(struct fs_statcache));
1378
0
  sc->sc_pool = sc_pool;
1379
0
  sc->sc_path = pstrndup(sc_pool, path, path_len);
1380
0
  memcpy(&(sc->sc_stat), st, sizeof(struct stat));
1381
0
  sc->sc_errno = xerrno;
1382
0
  sc->sc_retval = retval;
1383
0
  sc->sc_cached_ts = now;
1384
1385
0
  res = pr_table_add(cache_tab, sc->sc_path, sc, sizeof(struct fs_statcache *));
1386
0
  if (res < 0) {
1387
0
    int tmp_errno = errno;
1388
1389
0
    if (tmp_errno == EEXIST) {
1390
0
      res = 0;
1391
0
    }
1392
1393
0
    destroy_pool(sc->sc_pool);
1394
0
    errno = tmp_errno;
1395
1396
0
  } else {
1397
0
    xaset_insert_end(cache_set, (xasetmember_t *) sc);
1398
0
  }
1399
1400
0
  return (res == 0 ? 1 : res);
1401
0
}
1402
1403
static int cache_stat(pr_fs_t *fs, const char *path, struct stat *st,
1404
0
    unsigned int op) {
1405
0
  int res = -1, retval, xerrno = 0;
1406
0
  char cleaned_path[PR_TUNABLE_PATH_MAX+1], pathbuf[PR_TUNABLE_PATH_MAX+1];
1407
0
  int (*mystat)(pr_fs_t *, const char *, struct stat *) = NULL;
1408
0
  size_t path_len;
1409
0
  pr_table_t *cache_tab = NULL;
1410
0
  xaset_t *cache_set = NULL;
1411
0
  const struct fs_statcache *sc = NULL;
1412
0
  time_t now;
1413
1414
0
  now = time(NULL);
1415
0
  memset(cleaned_path, '\0', sizeof(cleaned_path));
1416
0
  memset(pathbuf, '\0', sizeof(pathbuf));
1417
1418
0
  if (fs->non_std_path == FALSE) {
1419
    /* Use only absolute path names.  Construct them, if given a relative
1420
     * path, based on cwd.  This obviates the need for something like
1421
     * realpath(3), which only introduces more stat(2) system calls.
1422
     */
1423
0
    if (*path != '/') {
1424
0
      size_t pathbuf_len;
1425
1426
0
      sstrcat(pathbuf, cwd, sizeof(pathbuf)-1);
1427
0
      pathbuf_len = cwd_len;
1428
1429
      /* If the cwd is "/", we don't need to duplicate the path separator.
1430
       * On some systems (e.g. Cygwin), this duplication can cause problems,
1431
       * as the path may then have different semantics.
1432
       */
1433
0
      if (strncmp(cwd, "/", 2) != 0) {
1434
0
        sstrcat(pathbuf + pathbuf_len, "/", sizeof(pathbuf) - pathbuf_len - 1);
1435
0
        pathbuf_len++;
1436
0
      }
1437
1438
      /* If the given directory is ".", then we don't need to append it. */
1439
0
      if (strncmp(path, ".", 2) != 0) {
1440
0
        sstrcat(pathbuf + pathbuf_len, path, sizeof(pathbuf)- pathbuf_len - 1);
1441
0
      }
1442
1443
0
    } else {
1444
0
      sstrncpy(pathbuf, path, sizeof(pathbuf)-1);
1445
0
    }
1446
1447
0
    pr_fs_clean_path2(pathbuf, cleaned_path, sizeof(cleaned_path)-1, 0);
1448
1449
0
  } else {
1450
0
    sstrncpy(cleaned_path, path, sizeof(cleaned_path)-1);
1451
0
  }
1452
1453
  /* Determine which filesystem function to use, stat() or lstat() */
1454
0
  if (op == FSIO_FILE_STAT) {
1455
0
    mystat = fs->stat ? fs->stat : sys_stat;
1456
0
    cache_tab = stat_statcache_tab;
1457
0
    cache_set = stat_statcache_set;
1458
1459
0
  } else {
1460
0
    mystat = fs->lstat ? fs->lstat : sys_lstat;
1461
0
    cache_tab = lstat_statcache_tab;
1462
0
    cache_set = lstat_statcache_set;
1463
0
  }
1464
1465
0
  path_len = strlen(cleaned_path);
1466
1467
0
  sc = fs_statcache_get(cache_tab, cache_set, cleaned_path, path_len, now);
1468
0
  if (sc != NULL) {
1469
    /* Update the given struct stat pointer with the cached info */
1470
0
    memcpy(st, &(sc->sc_stat), sizeof(struct stat));
1471
1472
0
    pr_trace_msg(trace_channel, 18,
1473
0
      "using cached stat for %s for path '%s' (retval %d, errno %s)",
1474
0
      op == FSIO_FILE_STAT ? "stat()" : "lstat()", path, sc->sc_retval,
1475
0
      strerror(sc->sc_errno));
1476
1477
    /* Use the cached errno as well */
1478
0
    errno = sc->sc_errno;
1479
1480
0
    return sc->sc_retval;
1481
0
  }
1482
1483
0
  pr_trace_msg(trace_channel, 8, "using %s %s for path '%s'",
1484
0
    fs->fs_name, op == FSIO_FILE_STAT ? "stat()" : "lstat()", path);
1485
0
  retval = mystat(fs, cleaned_path, st);
1486
0
  xerrno = errno;
1487
1488
0
  if (retval == 0) {
1489
0
    xerrno = 0;
1490
0
  }
1491
1492
  /* Update the cache */
1493
0
  res = fs_statcache_add(cache_tab, cache_set, cleaned_path, path_len, st,
1494
0
    xerrno, retval, now);
1495
0
  if (res < 0) {
1496
0
    pr_trace_msg(trace_channel, 8,
1497
0
      "error adding cached stat for '%s': %s", cleaned_path, strerror(errno));
1498
1499
0
  } else if (res > 0) {
1500
0
    pr_trace_msg(trace_channel, 18,
1501
0
      "added cached stat for path '%s' (retval %d, errno %s)", path,
1502
0
      retval, strerror(xerrno));
1503
0
  }
1504
1505
0
  if (retval < 0) {
1506
0
    errno = xerrno;
1507
0
  }
1508
1509
0
  return retval;
1510
0
}
1511
1512
/* Lookup routines */
1513
1514
/* Necessary prototype for static function */
1515
static pr_fs_t *lookup_file_canon_fs(const char *, char **, int);
1516
1517
/* lookup_dir_fs() is called when we want to perform some sort of directory
1518
 * operation on a directory or file.  A "closest" match algorithm is used.  If
1519
 * the lookup fails or is not "close enough" (i.e. the final target does not
1520
 * exactly match an existing filesystem handle) scan the list of fs_matches for
1521
 * matchable targets and call any callback functions, then rescan the pr_fs_t
1522
 * list.  The rescan is performed in case any modules registered pr_fs_ts
1523
 * during the hit.
1524
 */
1525
0
static pr_fs_t *lookup_dir_fs(const char *path, int op) {
1526
0
  char buf[PR_TUNABLE_PATH_MAX + 1], tmp_path[PR_TUNABLE_PATH_MAX + 1];
1527
0
  pr_fs_t *fs = NULL;
1528
0
  int exact = FALSE;
1529
0
  size_t tmp_pathlen = 0;
1530
1531
0
  memset(buf, '\0', sizeof(buf));
1532
0
  memset(tmp_path, '\0', sizeof(tmp_path));
1533
0
  sstrncpy(buf, path, sizeof(buf));
1534
1535
  /* Check if the given path is an absolute path.  Since there may be
1536
   * alternate fs roots, this is not a simple check.  If the path is
1537
   * not absolute, prepend the current location.
1538
   */
1539
0
  if (pr_fs_valid_path(path) < 0) {
1540
0
    if (pr_fs_dircat(tmp_path, sizeof(tmp_path), cwd, buf) < 0) {
1541
0
      return NULL;
1542
0
    }
1543
1544
0
  } else {
1545
0
    sstrncpy(tmp_path, buf, sizeof(tmp_path));
1546
0
  }
1547
1548
  /* Make sure that if this is a directory operation, the path being
1549
   * search ends in a trailing slash -- this is how files and directories
1550
   * are differentiated in the fs_map.
1551
   */
1552
0
  tmp_pathlen = strlen(tmp_path);
1553
0
  if ((FSIO_DIR_COMMON & op) &&
1554
0
      tmp_pathlen > 0 &&
1555
0
      tmp_pathlen < sizeof(tmp_path) &&
1556
0
      tmp_path[tmp_pathlen - 1] != '/') {
1557
0
    sstrcat(tmp_path, "/", sizeof(tmp_path));
1558
0
  }
1559
1560
0
  fs = pr_get_fs(tmp_path, &exact);
1561
0
  if (fs == NULL) {
1562
0
    fs = root_fs;
1563
0
  }
1564
1565
0
  return fs;
1566
0
}
1567
1568
/* lookup_file_fs() performs the same function as lookup_dir_fs, however
1569
 * because we are performing a file lookup, the target is the subdirectory
1570
 * _containing_ the actual target.  A basic optimization is used here,
1571
 * if the path contains no '/' characters, fs_cwd is returned.
1572
 */
1573
0
static pr_fs_t *lookup_file_fs(const char *path, char **deref, int op) {
1574
0
  pr_fs_t *fs = fs_cwd;
1575
0
  struct stat st;
1576
0
  int (*mystat)(pr_fs_t *, const char *, struct stat *) = NULL, res;
1577
0
  char linkbuf[PR_TUNABLE_PATH_MAX + 1];
1578
1579
0
  if (strchr(path, '/') != NULL) {
1580
0
    return lookup_dir_fs(path, op);
1581
0
  }
1582
1583
  /* Determine which function to use, stat() or lstat(). */
1584
0
  if (op == FSIO_FILE_STAT) {
1585
0
    while (fs != NULL &&
1586
0
           fs->fs_next != NULL &&
1587
0
           fs->stat == NULL) {
1588
0
      fs = fs->fs_next;
1589
0
    }
1590
1591
0
    mystat = fs->stat;
1592
1593
0
  } else {
1594
0
    while (fs != NULL &&
1595
0
           fs->fs_next != NULL &&
1596
0
           fs->lstat == NULL) {
1597
0
      fs = fs->fs_next;
1598
0
    }
1599
1600
0
    mystat = fs->lstat;
1601
0
  }
1602
1603
0
  if (mystat == NULL) {
1604
0
    errno = ENOSYS;
1605
0
    return NULL;
1606
0
  }
1607
1608
0
  res = mystat(fs, path, &st);
1609
0
  if (res < 0) {
1610
0
    return fs;
1611
0
  }
1612
1613
0
  if (!S_ISLNK(st.st_mode)) {
1614
0
    return fs;
1615
0
  }
1616
1617
  /* The given path is a symbolic link, in which case we need to find
1618
   * the actual path referenced, and return an pr_fs_t for _that_ path
1619
   */
1620
1621
  /* Three characters are reserved at the end of linkbuf for some path
1622
   * characters (and a trailing NUL).
1623
   */
1624
0
  if (fs->readlink != NULL) {
1625
0
    res = (fs->readlink)(fs, path, &linkbuf[2], sizeof(linkbuf)-3);
1626
1627
0
  } else {
1628
0
    errno = ENOSYS;
1629
0
    res = -1;
1630
0
  }
1631
1632
0
  if (res != -1) {
1633
0
    linkbuf[res] = '\0';
1634
1635
0
    if (strchr(linkbuf, '/') == NULL) {
1636
0
      if (res + 3 > PR_TUNABLE_PATH_MAX) {
1637
0
        res = PR_TUNABLE_PATH_MAX - 3;
1638
0
      }
1639
1640
0
      memmove(&linkbuf[2], linkbuf, res + 1);
1641
1642
0
      linkbuf[res+2] = '\0';
1643
0
      linkbuf[0] = '.';
1644
0
      linkbuf[1] = '/';
1645
0
      return lookup_file_canon_fs(linkbuf, deref, op);
1646
0
    }
1647
0
  }
1648
1649
  /* What happens if fs_cwd->readlink is NULL, or readlink() returns -1?
1650
   * I guess, for now, we punt, and return fs_cwd.
1651
   */
1652
0
  return fs_cwd;
1653
0
}
1654
1655
0
static pr_fs_t *lookup_file_canon_fs(const char *path, char **deref, int op) {
1656
0
  static char workpath[PR_TUNABLE_PATH_MAX + 1];
1657
1658
0
  memset(workpath,'\0',sizeof(workpath));
1659
1660
0
  if (pr_fs_resolve_partial(path, workpath, sizeof(workpath)-1,
1661
0
      FSIO_FILE_OPEN) == -1) {
1662
0
    if (*path == '/' || *path == '~') {
1663
0
      if (pr_fs_interpolate(path, workpath, sizeof(workpath)-1) != -1) {
1664
0
        sstrncpy(workpath, path, sizeof(workpath));
1665
0
      }
1666
1667
0
    } else {
1668
0
      if (pr_fs_dircat(workpath, sizeof(workpath), cwd, path) < 0) {
1669
0
        return NULL;
1670
0
      }
1671
0
    }
1672
0
  }
1673
1674
0
  if (deref) {
1675
0
    *deref = workpath;
1676
0
  }
1677
1678
0
  return lookup_file_fs(workpath, deref, op);
1679
0
}
1680
1681
/* FS Statcache API */
1682
1683
0
static void statcache_dumpf(const char *fmt, ...) {
1684
0
  char buf[PR_TUNABLE_BUFFER_SIZE];
1685
0
  va_list msg;
1686
1687
0
  memset(buf, '\0', sizeof(buf));
1688
1689
0
  va_start(msg, fmt);
1690
0
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
1691
0
  va_end(msg);
1692
1693
0
  buf[sizeof(buf)-1] = '\0';
1694
0
  (void) pr_trace_msg(statcache_channel, 9, "%s", buf);
1695
0
}
1696
1697
0
void pr_fs_statcache_dump(void) {
1698
0
  pr_table_dump(statcache_dumpf, stat_statcache_tab);
1699
0
  pr_table_dump(statcache_dumpf, lstat_statcache_tab);
1700
0
}
1701
1702
0
void pr_fs_statcache_free(void) {
1703
0
  if (stat_statcache_tab != NULL) {
1704
0
    int size;
1705
1706
0
    size = pr_table_count(stat_statcache_tab);
1707
0
    pr_trace_msg(statcache_channel, 11,
1708
0
      "resetting stat(2) statcache (clearing %d %s)", size,
1709
0
      size != 1 ? "entries" : "entry");
1710
0
    pr_table_empty(stat_statcache_tab);
1711
0
    pr_table_free(stat_statcache_tab);
1712
0
    stat_statcache_tab = NULL;
1713
0
    stat_statcache_set = NULL;
1714
0
  }
1715
1716
0
  if (lstat_statcache_tab != NULL) {
1717
0
    int size;
1718
1719
0
    size = pr_table_count(lstat_statcache_tab);
1720
0
    pr_trace_msg(statcache_channel, 11,
1721
0
      "resetting lstat(2) statcache (clearing %d %s)", size,
1722
0
      size != 1 ? "entries" : "entry");
1723
0
    pr_table_empty(lstat_statcache_tab);
1724
0
    pr_table_free(lstat_statcache_tab);
1725
0
    lstat_statcache_tab = NULL;
1726
0
    lstat_statcache_set = NULL;
1727
0
  }
1728
1729
  /* Note: we do not need to explicitly destroy each entry in the statcache
1730
   * tables, since ALL entries are allocated out of this statcache_pool.
1731
   * And we destroy this pool here.  Much easier cleanup that way.
1732
   */
1733
0
  if (statcache_pool != NULL) {
1734
0
    destroy_pool(statcache_pool);
1735
0
    statcache_pool = NULL;
1736
0
  }
1737
0
}
1738
1739
0
void pr_fs_statcache_reset(void) {
1740
0
  pr_fs_statcache_free();
1741
1742
0
  if (statcache_pool == NULL) {
1743
0
    statcache_pool = make_sub_pool(permanent_pool);
1744
0
    pr_pool_tag(statcache_pool, "FS Statcache Pool");
1745
0
  }
1746
1747
0
  stat_statcache_tab = pr_table_alloc(statcache_pool, 0);
1748
0
  stat_statcache_set = xaset_create(statcache_pool, NULL);
1749
1750
0
  lstat_statcache_tab = pr_table_alloc(statcache_pool, 0);
1751
0
  lstat_statcache_set = xaset_create(statcache_pool, NULL);
1752
0
}
1753
1754
int pr_fs_statcache_set_policy(unsigned int size, unsigned int max_age,
1755
0
    unsigned int flags) {
1756
1757
0
  statcache_size = size;
1758
0
  statcache_max_age = max_age;
1759
0
  statcache_flags = flags;
1760
1761
0
  return 0;
1762
0
}
1763
1764
0
int pr_fs_clear_cache2(const char *path) {
1765
0
  int res;
1766
1767
0
  (void) pr_event_generate("fs.statcache.clear", path);
1768
1769
0
  if (pr_table_count(stat_statcache_tab) == 0 &&
1770
0
      pr_table_count(lstat_statcache_tab) == 0) {
1771
0
    return 0;
1772
0
  }
1773
1774
0
  if (path != NULL) {
1775
0
    char cleaned_path[PR_TUNABLE_PATH_MAX+1], pathbuf[PR_TUNABLE_PATH_MAX+1];
1776
0
    int lstat_count, stat_count;
1777
1778
0
    if (*path != '/') {
1779
0
      size_t pathbuf_len;
1780
1781
0
      memset(cleaned_path, '\0', sizeof(cleaned_path));
1782
0
      memset(pathbuf, '\0', sizeof(pathbuf));
1783
1784
0
      sstrcat(pathbuf, cwd, sizeof(pathbuf)-1);
1785
0
      pathbuf_len = cwd_len;
1786
1787
0
      if (strncmp(cwd, "/", 2) != 0) {
1788
0
        sstrcat(pathbuf + pathbuf_len, "/", sizeof(pathbuf) - pathbuf_len - 1);
1789
0
        pathbuf_len++;
1790
0
      }
1791
1792
0
      if (strncmp(path, ".", 2) != 0) {
1793
0
        sstrcat(pathbuf + pathbuf_len, path, sizeof(pathbuf)- pathbuf_len - 1);
1794
0
      }
1795
1796
0
    } else {
1797
0
      sstrncpy(pathbuf, path, sizeof(pathbuf)-1);
1798
0
    }
1799
1800
0
    pr_fs_clean_path2(pathbuf, cleaned_path, sizeof(cleaned_path)-1, 0);
1801
1802
0
    res = 0;
1803
1804
0
    stat_count = pr_table_exists(stat_statcache_tab, cleaned_path);
1805
0
    if (stat_count > 0) {
1806
0
      const struct fs_statcache *sc;
1807
1808
0
      sc = pr_table_remove(stat_statcache_tab, cleaned_path, NULL);
1809
0
      if (sc != NULL) {
1810
0
        (void) xaset_remove(stat_statcache_set, (xasetmember_t *) sc);
1811
0
        destroy_pool(sc->sc_pool);
1812
0
      }
1813
1814
0
      pr_trace_msg(statcache_channel, 17, "cleared stat(2) entry for '%s'",
1815
0
        path);
1816
0
      res += stat_count;
1817
0
    }
1818
1819
0
    lstat_count = pr_table_exists(lstat_statcache_tab, cleaned_path);
1820
0
    if (lstat_count > 0) {
1821
0
      const struct fs_statcache *sc;
1822
1823
0
      sc = pr_table_remove(lstat_statcache_tab, cleaned_path, NULL);
1824
0
      if (sc != NULL) {
1825
0
        (void) xaset_remove(lstat_statcache_set, (xasetmember_t *) sc);
1826
0
        destroy_pool(sc->sc_pool);
1827
0
      }
1828
1829
0
      pr_trace_msg(statcache_channel, 17, "cleared lstat(2) entry for '%s'",
1830
0
        path);
1831
0
      res += lstat_count;
1832
0
    }
1833
1834
0
  } else {
1835
    /* Caller is requesting that we empty the entire cache. */
1836
0
    pr_fs_statcache_reset();
1837
0
    res = 0;
1838
0
  }
1839
1840
0
  return res;
1841
0
}
1842
1843
0
void pr_fs_clear_cache(void) {
1844
0
  (void) pr_fs_clear_cache2(NULL);
1845
0
}
1846
1847
/* FS functions proper */
1848
1849
int pr_fs_copy_file2(const char *src, const char *dst, int flags,
1850
0
    void (*progress_cb)(int)) {
1851
0
  pr_fh_t *src_fh, *dst_fh;
1852
0
  struct stat src_st, dst_st;
1853
0
  char *buf;
1854
0
  size_t bufsz;
1855
0
  int dst_existed = FALSE, res;
1856
0
#ifdef PR_USE_XATTR
1857
0
  array_header *xattrs = NULL;
1858
0
#endif /* PR_USE_XATTR */
1859
1860
0
  if (src == NULL ||
1861
0
      dst == NULL) {
1862
0
    errno = EINVAL;
1863
0
    return -1;
1864
0
  }
1865
1866
0
  copy_iter_count = 0;
1867
1868
  /* Use a nonblocking open() for the path; it could be a FIFO, and we don't
1869
   * want to block forever if the other end of the FIFO is not running.
1870
   */
1871
0
  src_fh = pr_fsio_open(src, O_RDONLY|O_NONBLOCK);
1872
0
  if (src_fh == NULL) {
1873
0
    int xerrno = errno;
1874
1875
0
    pr_log_pri(PR_LOG_WARNING, "error opening source file '%s' "
1876
0
      "for copying: %s", src, strerror(xerrno));
1877
1878
0
    errno = xerrno;
1879
0
    return -1;
1880
0
  }
1881
1882
  /* Do not allow copying of directories. open(2) may not fail when
1883
   * opening the source path, since it is only doing a read-only open,
1884
   * which does work on directories.
1885
   */
1886
1887
  /* This should never fail. */
1888
0
  if (pr_fsio_fstat(src_fh, &src_st) < 0) {
1889
0
    pr_trace_msg(trace_channel, 3,
1890
0
      "error fstat'ing '%s': %s", src, strerror(errno));
1891
0
  }
1892
1893
0
  if (S_ISDIR(src_st.st_mode)) {
1894
0
    int xerrno = EISDIR;
1895
1896
0
    pr_fsio_close(src_fh);
1897
1898
0
    pr_log_pri(PR_LOG_WARNING, "warning: cannot copy source '%s': %s", src,
1899
0
      strerror(xerrno));
1900
1901
0
    errno = xerrno;
1902
0
    return -1;
1903
0
  }
1904
1905
0
  if (pr_fsio_set_block(src_fh) < 0) {
1906
0
    pr_trace_msg(trace_channel, 3,
1907
0
      "error putting '%s' into blocking mode: %s", src, strerror(errno));
1908
0
  }
1909
1910
  /* We use stat() here, not lstat(), since open() would follow a symlink
1911
   * to its target, and what we really want to know here is whether the
1912
   * ultimate destination file exists or not.
1913
   */
1914
0
  pr_fs_clear_cache2(dst);
1915
0
  if (pr_fsio_stat(dst, &dst_st) == 0) {
1916
0
    if (S_ISDIR(dst_st.st_mode)) {
1917
0
      int xerrno = EISDIR;
1918
1919
0
      (void) pr_fsio_close(src_fh);
1920
1921
0
      pr_log_pri(PR_LOG_WARNING,
1922
0
        "warning: cannot copy to destination '%s': %s", dst, strerror(xerrno));
1923
1924
0
      errno = xerrno;
1925
0
      return -1;
1926
0
    }
1927
1928
0
    dst_existed = TRUE;
1929
0
    pr_fs_clear_cache2(dst);
1930
0
  }
1931
1932
  /* Use a nonblocking open() for the path; it could be a FIFO, and we don't
1933
   * want to block forever if the other end of the FIFO is not running.
1934
   */
1935
0
  dst_fh = pr_fsio_open(dst, O_WRONLY|O_CREAT|O_NONBLOCK);
1936
0
  if (dst_fh == NULL) {
1937
0
    int xerrno = errno;
1938
1939
0
    (void) pr_fsio_close(src_fh);
1940
1941
0
    pr_log_pri(PR_LOG_WARNING, "error opening destination file '%s' "
1942
0
      "for copying: %s", dst, strerror(xerrno));
1943
1944
0
    errno = xerrno;
1945
0
    return -1;
1946
0
  }
1947
1948
0
  if (pr_fsio_set_block(dst_fh) < 0) {
1949
0
    pr_trace_msg(trace_channel, 3,
1950
0
      "error putting '%s' into blocking mode: %s", dst, strerror(errno));
1951
0
  }
1952
1953
  /* Stat the source file to find its optimal copy block size. */
1954
0
  if (pr_fsio_fstat(src_fh, &src_st) < 0) {
1955
0
    int xerrno = errno;
1956
1957
0
    pr_log_pri(PR_LOG_WARNING, "error checking source file '%s' "
1958
0
      "for copying: %s", src, strerror(xerrno));
1959
1960
0
    (void) pr_fsio_close(src_fh);
1961
0
    (void) pr_fsio_close(dst_fh);
1962
1963
    /* Don't unlink the destination file if it already existed. */
1964
0
    if (!dst_existed) {
1965
0
      if (!(flags & PR_FSIO_COPY_FILE_FL_NO_DELETE_ON_FAILURE)) {
1966
0
        if (pr_fsio_unlink(dst) < 0) {
1967
0
          pr_trace_msg(trace_channel, 12,
1968
0
            "error deleting failed copy of '%s': %s", dst, strerror(errno));
1969
0
        }
1970
0
      }
1971
0
    }
1972
1973
0
    errno = xerrno;
1974
0
    return -1;
1975
0
  }
1976
1977
0
  if (pr_fsio_fstat(dst_fh, &dst_st) == 0) {
1978
1979
    /* Check to see if the source and destination paths are identical.
1980
     * We wait until now, rather than simply comparing the path strings
1981
     * earlier, in order to do stats on the paths and compare things like
1982
     * file size, mtime, inode, etc.
1983
     */
1984
1985
0
    if (strcmp(src, dst) == 0 &&
1986
0
        src_st.st_dev == dst_st.st_dev &&
1987
0
        src_st.st_ino == dst_st.st_ino &&
1988
0
        src_st.st_size == dst_st.st_size &&
1989
0
        src_st.st_mtime == dst_st.st_mtime) {
1990
1991
0
      (void) pr_fsio_close(src_fh);
1992
0
      (void) pr_fsio_close(dst_fh);
1993
1994
      /* No need to copy the same file. */
1995
0
      return 0;
1996
0
    }
1997
0
  }
1998
1999
0
  bufsz = src_st.st_blksize;
2000
0
  buf = malloc(bufsz);
2001
0
  if (buf == NULL) {
2002
0
    pr_log_pri(PR_LOG_ALERT, "Out of memory!");
2003
0
    exit(1);
2004
0
  }
2005
2006
0
#ifdef S_ISFIFO
2007
0
  if (!S_ISFIFO(dst_st.st_mode)) {
2008
    /* Make sure the destination file starts with a zero size. */
2009
0
    pr_fsio_truncate(dst, 0);
2010
0
  }
2011
0
#endif
2012
2013
0
  while ((res = pr_fsio_read(src_fh, buf, bufsz)) > 0) {
2014
0
    size_t datalen;
2015
0
    off_t offset;
2016
2017
0
    pr_signals_handle();
2018
2019
    /* Be sure to handle short writes. */
2020
0
    datalen = res;
2021
0
    offset = 0;
2022
2023
0
    while (datalen > 0) {
2024
0
      res = pr_fsio_write(dst_fh, buf + offset, datalen);
2025
0
      if (res < 0) {
2026
0
        int xerrno = errno;
2027
2028
0
        if (xerrno == EINTR ||
2029
0
            xerrno == EAGAIN) {
2030
0
          pr_signals_handle();
2031
0
          continue;
2032
0
        }
2033
2034
0
        (void) pr_fsio_close(src_fh);
2035
0
        (void) pr_fsio_close(dst_fh);
2036
2037
        /* Don't unlink the destination file if it already existed. */
2038
0
        if (!dst_existed) {
2039
0
          if (!(flags & PR_FSIO_COPY_FILE_FL_NO_DELETE_ON_FAILURE)) {
2040
0
            if (pr_fsio_unlink(dst) < 0) {
2041
0
              pr_trace_msg(trace_channel, 12,
2042
0
                "error deleting failed copy of '%s': %s", dst, strerror(errno));
2043
0
            }
2044
0
          }
2045
0
        }
2046
2047
0
        pr_log_pri(PR_LOG_WARNING, "error copying to '%s': %s", dst,
2048
0
          strerror(xerrno));
2049
0
        free(buf);
2050
2051
0
        errno = xerrno;
2052
0
        return -1;
2053
0
      }
2054
2055
0
      if (progress_cb != NULL) {
2056
0
        (progress_cb)(res);
2057
2058
0
      } else {
2059
0
        copy_progress_cb(res);
2060
0
      }
2061
2062
0
      if ((size_t) res == datalen) {
2063
0
        break;
2064
0
      }
2065
2066
0
      offset += res;
2067
0
      datalen -= res;
2068
0
    }
2069
0
  }
2070
2071
0
  free(buf);
2072
2073
#if defined(HAVE_POSIX_ACL) && defined(PR_USE_FACL)
2074
  {
2075
    /* Copy any ACLs from the source file to the destination file as well. */
2076
# if defined(HAVE_BSD_POSIX_ACL)
2077
    acl_t facl, facl_dup = NULL;
2078
    int have_facl = FALSE, have_dup = FALSE;
2079
2080
    facl = acl_get_fd(PR_FH_FD(src_fh));
2081
    if (facl) {
2082
      have_facl = TRUE;
2083
    }
2084
2085
    if (have_facl) {
2086
      facl_dup = acl_dup(facl);
2087
    }
2088
2089
    if (facl_dup) {
2090
      have_dup = TRUE;
2091
    }
2092
2093
    if (have_dup &&
2094
        acl_set_fd(PR_FH_FD(dst_fh), facl_dup) < 0) {
2095
      pr_log_debug(DEBUG3, "error applying ACL to destination file: %s",
2096
        strerror(errno));
2097
    }
2098
2099
    if (have_dup) {
2100
      acl_free(facl_dup);
2101
    }
2102
# elif defined(HAVE_LINUX_POSIX_ACL)
2103
2104
#  if defined(HAVE_PERM_COPY_FD)
2105
    /* Linux provides the handy perm_copy_fd(3) function in its libacl
2106
     * library just for this purpose.
2107
     */
2108
    if (perm_copy_fd(src, PR_FH_FD(src_fh), dst, PR_FH_FD(dst_fh), NULL) < 0) {
2109
      pr_log_debug(DEBUG3, "error copying ACL to destination file: %s",
2110
        strerror(errno));
2111
    }
2112
2113
#  else
2114
    acl_t src_acl = acl_get_fd(PR_FH_FD(src_fh));
2115
    if (src_acl == NULL) {
2116
      pr_log_debug(DEBUG3, "error obtaining ACL for fd %d: %s",
2117
        PR_FH_FD(src_fh), strerror(errno));
2118
2119
    } else {
2120
      if (acl_set_fd(PR_FH_FD(dst_fh), src_acl) < 0) {
2121
        pr_log_debug(DEBUG3, "error setting ACL on fd %d: %s",
2122
          PR_FH_FD(dst_fh), strerror(errno));
2123
2124
      } else {
2125
        acl_free(src_acl);
2126
      }
2127
    }
2128
2129
#  endif /* !HAVE_PERM_COPY_FD */
2130
2131
# elif defined(HAVE_SOLARIS_POSIX_ACL)
2132
    int nents;
2133
2134
    nents = facl(PR_FH_FD(src_fh), GETACLCNT, 0, NULL);
2135
    if (nents < 0) {
2136
      pr_log_debug(DEBUG3, "error getting source file ACL count: %s",
2137
        strerror(errno));
2138
2139
    } else {
2140
      aclent_t *acls;
2141
2142
      acls = malloc(sizeof(aclent_t) * nents);
2143
      if (acls == NULL) {
2144
        pr_log_pri(PR_LOG_ALERT, "Out of memory!");
2145
        exit(1);
2146
      }
2147
2148
      if (facl(PR_FH_FD(src_fh), GETACL, nents, acls) < 0) {
2149
        pr_log_debug(DEBUG3, "error getting source file ACLs: %s",
2150
          strerror(errno));
2151
2152
      } else {
2153
        if (facl(PR_FH_FD(dst_fh), SETACL, nents, acls) < 0) {
2154
          pr_log_debug(DEBUG3, "error setting dest file ACLs: %s",
2155
            strerror(errno));
2156
        }
2157
      }
2158
2159
      free(acls);
2160
    }
2161
# endif /* HAVE_SOLARIS_POSIX_ACL && PR_USE_FACL */
2162
  }
2163
#endif /* HAVE_POSIX_ACL */
2164
2165
0
#ifdef PR_USE_XATTR
2166
  /* Copy any xattrs that the source file may have. We'll use the
2167
   * destination file handle's pool for our xattr allocations.
2168
   */
2169
0
  if (pr_fsio_flistxattr(dst_fh->fh_pool, src_fh, &xattrs) > 0) {
2170
0
    register unsigned int i;
2171
0
    const char **names;
2172
2173
0
    names = xattrs->elts;
2174
0
    for (i = 0; i < xattrs->nelts; i++) {
2175
0
      ssize_t valsz;
2176
2177
      /* First, find out how much memory we need for this attribute's
2178
       * value.
2179
       */
2180
0
      valsz = pr_fsio_fgetxattr(dst_fh->fh_pool, src_fh, names[i], NULL, 0);
2181
0
      if (valsz > 0) {
2182
0
        void *val;
2183
0
        ssize_t sz;
2184
2185
0
        val = palloc(dst_fh->fh_pool, valsz);
2186
0
        sz = pr_fsio_fgetxattr(dst_fh->fh_pool, src_fh, names[i], val, valsz);
2187
0
        if (sz > 0) {
2188
0
          sz = pr_fsio_fsetxattr(dst_fh->fh_pool, dst_fh, names[i], val,
2189
0
            valsz, 0);
2190
0
          if (sz < 0 &&
2191
0
              errno != ENOSYS) {
2192
0
            pr_trace_msg(trace_channel, 7,
2193
0
              "error copying xattr '%s' (%lu bytes) from '%s' to '%s': %s",
2194
0
              names[i], (unsigned long) valsz, src, dst, strerror(errno));
2195
0
          }
2196
0
        }
2197
0
      }
2198
0
    }
2199
0
  }
2200
0
#endif /* PR_USE_XATTR */
2201
2202
0
  (void) pr_fsio_close(src_fh);
2203
2204
0
  if (progress_cb != NULL) {
2205
0
    (progress_cb)(0);
2206
2207
0
  } else {
2208
0
    copy_progress_cb(0);
2209
0
  }
2210
2211
0
  res = pr_fsio_close(dst_fh);
2212
0
  if (res < 0) {
2213
0
    int xerrno = errno;
2214
2215
    /* Don't unlink the destination file if it already existed. */
2216
0
    if (!dst_existed) {
2217
0
      if (!(flags & PR_FSIO_COPY_FILE_FL_NO_DELETE_ON_FAILURE)) {
2218
0
        if (pr_fsio_unlink(dst) < 0) {
2219
0
          pr_trace_msg(trace_channel, 12,
2220
0
            "error deleting failed copy of '%s': %s", dst, strerror(errno));
2221
0
        }
2222
0
      }
2223
0
    }
2224
2225
0
    pr_log_pri(PR_LOG_WARNING, "error closing '%s': %s", dst,
2226
0
      strerror(xerrno));
2227
2228
0
    errno = xerrno;
2229
0
  }
2230
2231
0
  return res;
2232
0
}
2233
2234
0
int pr_fs_copy_file(const char *src, const char *dst) {
2235
0
  return pr_fs_copy_file2(src, dst, 0, NULL);
2236
0
}
2237
2238
pr_fs_t *pr_register_fs2(pool *p, const char *name, const char *path,
2239
0
    int flags) {
2240
0
  pr_fs_t *fs = NULL;
2241
0
  int xerrno = 0;
2242
2243
  /* Sanity check */
2244
0
  if (p == NULL ||
2245
0
      name == NULL ||
2246
0
      path == NULL) {
2247
0
    errno = EINVAL;
2248
0
    return NULL;
2249
0
  }
2250
2251
  /* Instantiate an pr_fs_t */
2252
0
  fs = pr_create_fs(p, name);
2253
0
  xerrno = errno;
2254
2255
0
  if (fs == NULL) {
2256
0
    pr_trace_msg(trace_channel, 6, "error creating FS '%s': %s", name,
2257
0
      strerror(errno));
2258
0
    errno = xerrno;
2259
0
    return NULL;
2260
0
  }
2261
2262
0
  if (flags & PR_FSIO_REGISTER_FL_INHERIT_HANDLERS) {
2263
0
    pr_fs_t *curr_fs = NULL;
2264
0
    int match = FALSE;
2265
2266
    /* Note that we need to be aware of other modules' FS handlers, such
2267
     * as mod_vroot (see Issue #1764, #1780).
2268
     */
2269
0
    curr_fs = pr_get_fs(path, &match);
2270
0
    if (curr_fs != NULL) {
2271
0
      fs->fs_name = pstrcat(fs->fs_pool, name, "+", curr_fs->fs_name, NULL);
2272
2273
      /* Inherit all of the current FS handlers.  This makes it easy to
2274
       * preserve the functionality desired by all previously registered
2275
       * handlers.
2276
       */
2277
0
      fs->stat = curr_fs->stat;
2278
0
      fs->fstat = curr_fs->fstat;
2279
0
      fs->lstat = curr_fs->lstat;
2280
0
      fs->rename = curr_fs->rename;
2281
0
      fs->unlink = curr_fs->unlink;
2282
0
      fs->open = curr_fs->open;
2283
0
      fs->close = curr_fs->close;
2284
0
      fs->read = curr_fs->read;
2285
0
      fs->pread = curr_fs->pread;
2286
0
      fs->write = curr_fs->write;
2287
0
      fs->pwrite = curr_fs->pwrite;
2288
0
      fs->lseek = curr_fs->lseek;
2289
0
      fs->link = curr_fs->link;
2290
0
      fs->readlink = curr_fs->readlink;
2291
0
      fs->symlink = curr_fs->symlink;
2292
0
      fs->ftruncate = curr_fs->ftruncate;
2293
0
      fs->truncate = curr_fs->truncate;
2294
0
      fs->chmod = curr_fs->chmod;
2295
0
      fs->fchmod = curr_fs->fchmod;
2296
0
      fs->chown = curr_fs->chown;
2297
0
      fs->fchown = curr_fs->fchown;
2298
0
      fs->lchown = curr_fs->lchown;
2299
0
      fs->access = curr_fs->access;
2300
0
      fs->faccess = curr_fs->faccess;
2301
0
      fs->utimes = curr_fs->utimes;
2302
0
      fs->futimes = curr_fs->futimes;
2303
0
      fs->fsync = curr_fs->fsync;
2304
2305
0
      fs->chdir = curr_fs->chdir;
2306
0
      fs->chroot = curr_fs->chroot;
2307
0
      fs->opendir = curr_fs->opendir;
2308
0
      fs->closedir = curr_fs->closedir;
2309
0
      fs->readdir = curr_fs->readdir;
2310
0
      fs->mkdir = curr_fs->mkdir;
2311
0
      fs->rmdir = curr_fs->rmdir;
2312
0
    }
2313
0
  }
2314
2315
0
  if (pr_insert_fs(fs, path) == FALSE) {
2316
0
    xerrno = errno;
2317
2318
0
    pr_trace_msg(trace_channel, 4, "error inserting FS '%s' at path '%s'",
2319
0
      name, path);
2320
2321
0
    destroy_pool(fs->fs_pool);
2322
0
    errno = xerrno;
2323
0
    return NULL;
2324
0
  }
2325
2326
0
  return fs;
2327
0
}
2328
2329
0
pr_fs_t *pr_register_fs(pool *p, const char *name, const char *path) {
2330
0
  return pr_register_fs2(p, name, path, PR_FSIO_REGISTER_FL_INHERIT_HANDLERS);
2331
0
}
2332
2333
0
pr_fs_t *pr_create_fs(pool *p, const char *name) {
2334
0
  pr_fs_t *fs = NULL;
2335
0
  pool *fs_pool = NULL;
2336
2337
  /* Sanity check */
2338
0
  if (p == NULL ||
2339
0
      name == NULL) {
2340
0
    errno = EINVAL;
2341
0
    return NULL;
2342
0
  }
2343
2344
  /* Allocate a subpool, then allocate an pr_fs_t object from that subpool */
2345
0
  fs_pool = make_sub_pool(p);
2346
0
  pr_pool_tag(fs_pool, "FS Pool");
2347
2348
0
  fs = pcalloc(fs_pool, sizeof(pr_fs_t));
2349
0
  fs->fs_pool = fs_pool;
2350
0
  fs->fs_next = fs->fs_prev = NULL;
2351
0
  fs->fs_name = pstrdup(fs->fs_pool, name);
2352
0
  fs->fs_original_name = fs->fs_name;
2353
0
  fs->fs_next = root_fs;
2354
0
  fs->allow_xdev_link = TRUE;
2355
0
  fs->allow_xdev_rename = TRUE;
2356
2357
  /* This is NULL until set by pr_insert_fs() */
2358
0
  fs->fs_path = NULL;
2359
2360
0
  return fs;
2361
0
}
2362
2363
0
int pr_insert_fs(pr_fs_t *fs, const char *path) {
2364
0
  char cleaned_path[PR_TUNABLE_PATH_MAX] = {'\0'};
2365
2366
0
  if (fs == NULL ||
2367
0
      path == NULL) {
2368
0
    errno = EINVAL;
2369
0
    return -1;
2370
0
  }
2371
2372
0
  if (fs_map == NULL) {
2373
0
    pool *map_pool = make_sub_pool(permanent_pool);
2374
0
    pr_pool_tag(map_pool, "FSIO Map Pool");
2375
2376
0
    fs_map = make_array(map_pool, 0, sizeof(pr_fs_t *));
2377
0
  }
2378
2379
  /* Clean the path, but only if it starts with a '/'.  Non-local-filesystem
2380
   * paths may not want/need to be cleaned.
2381
   */
2382
0
  if (*path == '/') {
2383
0
    pr_fs_clean_path(path, cleaned_path, sizeof(cleaned_path));
2384
2385
    /* Cleaning the path may have removed a trailing slash, which the
2386
     * caller may actually have wanted.  Make sure one is present in
2387
     * the cleaned version, if it was present in the original version and
2388
     * is not present in the cleaned version.
2389
     */
2390
0
    if (path[strlen(path)-1] == '/') {
2391
0
      size_t len = strlen(cleaned_path);
2392
2393
0
      if (len > 1 &&
2394
0
          len < (PR_TUNABLE_PATH_MAX-3) &&
2395
0
          cleaned_path[len-1] != '/') {
2396
0
        cleaned_path[len] = '/';
2397
0
        cleaned_path[len+1] = '\0';
2398
0
      }
2399
0
    }
2400
2401
0
  } else {
2402
0
    sstrncpy(cleaned_path, path, sizeof(cleaned_path));
2403
0
  }
2404
2405
0
  if (fs->fs_path == NULL) {
2406
0
    fs->fs_path = pstrdup(fs->fs_pool, cleaned_path);
2407
0
  }
2408
2409
  /* Check for duplicates. */
2410
0
  if (fs_map->nelts > 0) {
2411
0
    pr_fs_t *fsi = NULL, **fs_objs = (pr_fs_t **) fs_map->elts;
2412
0
    register unsigned int i;
2413
2414
0
    for (i = 0; i < fs_map->nelts; i++) {
2415
0
      fsi = fs_objs[i];
2416
2417
0
      if (strcmp(fsi->fs_path, cleaned_path) == 0) {
2418
        /* An entry for this path already exists.  Make sure the FS being
2419
         * mounted is not the same as the one already present.
2420
         */
2421
0
        if (strcmp(fsi->fs_original_name, fs->fs_original_name) == 0) {
2422
0
          pr_log_pri(PR_LOG_NOTICE,
2423
0
            "error: duplicate fs paths not allowed: '%s'", cleaned_path);
2424
0
          errno = EEXIST;
2425
0
          return FALSE;
2426
0
        }
2427
2428
        /* "Push" the given FS on top of the existing one. */
2429
0
        fs->fs_next = fsi;
2430
0
        fsi->fs_prev = fs;
2431
0
        fs_objs[i] = fs;
2432
2433
0
        chk_fs_map = TRUE;
2434
0
        return TRUE;
2435
0
      }
2436
0
    }
2437
0
  }
2438
2439
  /* Push the new FS into the container, then resort the contents. */
2440
0
  *((pr_fs_t **) push_array(fs_map)) = fs;
2441
2442
  /* Sort the FSs in the map according to their paths, but only if there
2443
   * are more than one element in the array_header.
2444
   */
2445
0
  if (fs_map->nelts > 1) {
2446
0
    qsort(fs_map->elts, fs_map->nelts, sizeof(pr_fs_t *), fs_cmp);
2447
0
  }
2448
2449
  /* Set the flag so that the fs wrapper functions know that a new FS
2450
   * has been registered.
2451
   */
2452
0
  chk_fs_map = TRUE;
2453
2454
0
  return TRUE;
2455
0
}
2456
2457
0
pr_fs_t *pr_unmount_fs(const char *path, const char *name) {
2458
0
  pr_fs_t *fsi = NULL, **fs_objs = NULL;
2459
0
  register unsigned int i = 0;
2460
2461
  /* Sanity check */
2462
0
  if (path == NULL) {
2463
0
    errno = EINVAL;
2464
0
    return NULL;
2465
0
  }
2466
2467
  /* This should never be called before pr_register_fs(), but, just in case...*/
2468
0
  if (fs_map == NULL) {
2469
0
    errno = EACCES;
2470
0
    return NULL;
2471
0
  }
2472
2473
0
  fs_objs = (pr_fs_t **) fs_map->elts;
2474
2475
0
  for (i = 0; i < fs_map->nelts; i++) {
2476
0
    fsi = fs_objs[i];
2477
2478
0
    if (strcmp(fsi->fs_path, path) == 0 &&
2479
0
        (name ? strcmp(fsi->fs_original_name, name) == 0 : TRUE)) {
2480
2481
      /* Exact match -- remove this FS.  If there is an FS underneath, pop
2482
       * the top FS off the stack.  Otherwise, allocate a new map.  Then
2483
       * iterate through the old map, pushing all other FSs into the new map.
2484
       * Destroy the old map.  Move the new map into place.
2485
       */
2486
2487
0
      if (fsi->fs_next == NULL) {
2488
0
        register unsigned int j = 0;
2489
0
        pr_fs_t *tmp_fs, **old_objs = NULL;
2490
0
        pool *map_pool;
2491
0
        array_header *new_map;
2492
2493
        /* If removing this FS would leave an empty map, don't bother
2494
         * allocating a new one.
2495
         */
2496
0
        if (fs_map->nelts == 1) {
2497
0
          destroy_pool(fs_map->pool);
2498
0
          fs_map = NULL;
2499
0
          fs_cwd = root_fs;
2500
2501
0
          chk_fs_map = TRUE;
2502
0
          return NULL;
2503
0
        }
2504
2505
0
        map_pool = make_sub_pool(permanent_pool);
2506
0
        new_map = make_array(map_pool, 0, sizeof(pr_fs_t *));
2507
2508
0
        pr_pool_tag(map_pool, "FSIO Map Pool");
2509
0
        old_objs = (pr_fs_t **) fs_map->elts;
2510
2511
0
        for (j = 0; j < fs_map->nelts; j++) {
2512
0
          tmp_fs = old_objs[j];
2513
2514
0
          if (strcmp(tmp_fs->fs_path, path) != 0) {
2515
0
            *((pr_fs_t **) push_array(new_map)) = old_objs[j];
2516
0
          }
2517
0
        }
2518
2519
0
        destroy_pool(fs_map->pool);
2520
0
        fs_map = new_map;
2521
2522
        /* Don't forget to set the flag so that wrapper functions scan the
2523
         * new map.
2524
         */
2525
0
        chk_fs_map = TRUE;
2526
2527
0
        return fsi;
2528
0
      }
2529
2530
      /* "Pop" this FS off the stack. */
2531
0
      if (fsi->fs_next != NULL) {
2532
0
        fsi->fs_next->fs_prev = NULL;
2533
0
      }
2534
0
      fs_objs[i] = fsi->fs_next;
2535
0
      fsi->fs_next = fsi->fs_prev = NULL;
2536
2537
0
      chk_fs_map = TRUE;
2538
0
      return fsi;
2539
0
    }
2540
0
  }
2541
2542
0
  errno = ENOENT;
2543
0
  return NULL;
2544
0
}
2545
2546
0
pr_fs_t *pr_remove_fs(const char *path) {
2547
0
  return pr_unmount_fs(path, NULL);
2548
0
}
2549
2550
0
int pr_unregister_fs(const char *path) {
2551
0
  pr_fs_t *fs = NULL;
2552
2553
0
  if (path == NULL) {
2554
0
    errno = EINVAL;
2555
0
    return -1;
2556
0
  }
2557
2558
  /* Call pr_remove_fs() to get the fs for this path removed from the map. */
2559
0
  fs = pr_remove_fs(path);
2560
0
  if (fs != NULL) {
2561
0
    destroy_pool(fs->fs_pool);
2562
0
    return 0;
2563
0
  }
2564
2565
0
  errno = ENOENT;
2566
0
  return -1;
2567
0
}
2568
2569
/* This function returns the best pr_fs_t to handle the given path.  It will
2570
 * return NULL if there are no registered pr_fs_ts to handle the given path,
2571
 * in which case the default root_fs should be used.  This is so that
2572
 * functions can look to see if an pr_fs_t, other than the default, for a
2573
 * given path has been registered, if necessary.  If the return value is
2574
 * non-NULL, that will be a registered pr_fs_t to handle the given path.  In
2575
 * this case, if the exact argument is not NULL, it will either be TRUE,
2576
 * signifying that the returned pr_fs_t is an exact match for the given
2577
 * path, or FALSE, meaning the returned pr_fs_t is a "best match" -- most
2578
 * likely the pr_fs_t that handles the directory in which the given path
2579
 * occurs.
2580
 */
2581
0
pr_fs_t *pr_get_fs(const char *path, int *exact) {
2582
0
  pr_fs_t *fs = NULL, **fs_objs = NULL, *best_match_fs = NULL;
2583
0
  register unsigned int i = 0;
2584
2585
  /* Sanity check */
2586
0
  if (path == NULL) {
2587
0
    errno = EINVAL;
2588
0
    return NULL;
2589
0
  }
2590
2591
  /* Basic optimization -- if there're no elements in the fs_map,
2592
   * return the root_fs.
2593
   */
2594
0
  if (fs_map == NULL ||
2595
0
      fs_map->nelts == 0) {
2596
0
    return root_fs;
2597
0
  }
2598
2599
0
  fs_objs = (pr_fs_t **) fs_map->elts;
2600
0
  best_match_fs = root_fs;
2601
2602
  /* In order to handle deferred-resolution paths (eg "~" paths), the given
2603
   * path will need to be passed through dir_realpath(), if necessary.
2604
   *
2605
   * The chk_fs_map flag, if TRUE, should be cleared on return of this
2606
   * function -- all that flag says is, if TRUE, that this function _might_
2607
   * return something different than it did on a previous call.
2608
   */
2609
2610
0
  for (i = 0; i < fs_map->nelts; i++) {
2611
0
    int res = 0;
2612
2613
0
    fs = fs_objs[i];
2614
2615
    /* If the current pr_fs_t's path ends in a slash (meaning it is a
2616
     * directory, and it matches the first part of the given path,
2617
     * assume it to be the best pr_fs_t found so far.
2618
     */
2619
0
    if ((fs->fs_path)[strlen(fs->fs_path) - 1] == '/' &&
2620
0
        !strncmp(path, fs->fs_path, strlen(fs->fs_path))) {
2621
0
      best_match_fs = fs;
2622
0
    }
2623
2624
0
    res = strcmp(fs->fs_path, path);
2625
0
    if (res == 0) {
2626
      /* Exact match */
2627
0
      if (exact) {
2628
0
        *exact = TRUE;
2629
0
      }
2630
2631
0
      chk_fs_map = FALSE;
2632
0
      return fs;
2633
0
    }
2634
2635
0
    if (res > 0) {
2636
0
      if (exact != NULL) {
2637
0
        *exact = FALSE;
2638
0
      }
2639
2640
0
      chk_fs_map = FALSE;
2641
2642
      /* Gone too far - return the best-match pr_fs_t */
2643
0
      return best_match_fs;
2644
0
    }
2645
0
  }
2646
2647
0
  chk_fs_map = FALSE;
2648
2649
0
  if (exact != NULL) {
2650
0
    *exact = FALSE;
2651
0
  }
2652
2653
  /* Return best-match by default */
2654
0
  return best_match_fs;
2655
0
}
2656
2657
0
int pr_fs_setcwd(const char *dir) {
2658
0
  if (pr_fs_resolve_path(dir, cwd, sizeof(cwd)-1, FSIO_DIR_CHDIR) < 0) {
2659
0
    return -1;
2660
0
  }
2661
2662
0
  if (sstrncpy(cwd, dir, sizeof(cwd)) < 0) {
2663
0
    return -1;
2664
0
  }
2665
2666
0
  fs_cwd = lookup_dir_fs(cwd, FSIO_DIR_CHDIR);
2667
0
  cwd[sizeof(cwd) - 1] = '\0';
2668
0
  cwd_len = strlen(cwd);
2669
2670
0
  return 0;
2671
0
}
2672
2673
0
const char *pr_fs_getcwd(void) {
2674
0
  return cwd;
2675
0
}
2676
2677
0
const char *pr_fs_getvwd(void) {
2678
0
  return vwd;
2679
0
}
2680
2681
0
int pr_fs_dircat(char *buf, int buflen, const char *dir1, const char *dir2) {
2682
  /* Make temporary copies so that memory areas can overlap */
2683
0
  char *_dir1 = NULL, *_dir2 = NULL, *ptr = NULL;
2684
0
  size_t dir1len = 0, dir2len = 0;
2685
2686
  /* The shortest possible path is "/", which requires 2 bytes. */
2687
2688
0
  if (buf == NULL ||
2689
0
      buflen < 2 ||
2690
0
      dir1 == NULL ||
2691
0
      dir2 == NULL) {
2692
0
    errno = EINVAL;
2693
0
    return -1;
2694
0
  }
2695
2696
  /* This is a test to see if we've got reasonable directories to concatenate.
2697
   */
2698
0
  dir1len = strlen(dir1);
2699
0
  dir2len = strlen(dir2);
2700
2701
  /* If both strings are empty, then the "concatenation" becomes trivial. */
2702
0
  if (dir1len == 0 &&
2703
0
      dir2len == 0) {
2704
0
    buf[0] = '/';
2705
0
    buf[1] = '\0';
2706
0
    return 0;
2707
0
  }
2708
2709
  /* If dir2 is non-empty, but dir1 IS empty... */
2710
0
  if (dir1len == 0) {
2711
0
    sstrncpy(buf, dir2, buflen);
2712
0
    buflen -= dir2len;
2713
0
    sstrcat(buf, "/", buflen);
2714
0
    return 0;
2715
0
  }
2716
2717
  /* Likewise, if dir1 is non-empty, but dir2 IS empty... */
2718
0
  if (dir2len == 0) {
2719
0
    sstrncpy(buf, dir1, buflen);
2720
0
    buflen -= dir1len;
2721
0
    sstrcat(buf, "/", buflen);
2722
0
    return 0;
2723
0
  }
2724
2725
0
  if ((dir1len + dir2len + 1) >= PR_TUNABLE_PATH_MAX) {
2726
0
    errno = ENAMETOOLONG;
2727
0
    buf[0] = '\0';
2728
0
    return -1;
2729
0
  }
2730
2731
0
  _dir1 = strdup(dir1);
2732
0
  if (_dir1 == NULL) {
2733
0
    return -1;
2734
0
  }
2735
2736
0
  _dir2 = strdup(dir2);
2737
0
  if (_dir2 == NULL) {
2738
0
    int xerrno = errno;
2739
2740
0
    free(_dir1);
2741
2742
0
    errno = xerrno;
2743
0
    return -1;
2744
0
  }
2745
2746
0
  if (*_dir2 == '/') {
2747
0
    sstrncpy(buf, _dir2, buflen);
2748
0
    free(_dir1);
2749
0
    free(_dir2);
2750
0
    return 0;
2751
0
  }
2752
2753
0
  ptr = buf;
2754
0
  sstrncpy(ptr, _dir1, buflen);
2755
0
  ptr += dir1len;
2756
0
  buflen -= dir1len;
2757
2758
0
  if (buflen > 0 &&
2759
0
      *(_dir1 + (dir1len-1)) != '/') {
2760
0
    sstrcat(ptr, "/", buflen);
2761
0
    ptr += 1;
2762
0
    buflen -= 1;
2763
0
  }
2764
2765
0
  sstrcat(ptr, _dir2, buflen);
2766
2767
0
  if (*buf == '\0') {
2768
0
   *buf++ = '/';
2769
0
   *buf = '\0';
2770
0
  }
2771
2772
0
  free(_dir1);
2773
0
  free(_dir2);
2774
2775
0
  return 0;
2776
0
}
2777
2778
/* This function performs any tilde expansion needed and then returns the
2779
 * resolved path, if any.
2780
 *
2781
 * Returns: -1 (errno = ENOENT): user does not exist
2782
 *           0 : no interpolation done (path exists)
2783
 *           1 : interpolation done
2784
 */
2785
0
int pr_fs_interpolate(const char *path, char *buf, size_t buflen) {
2786
0
  char *ptr = NULL;
2787
0
  size_t currlen, pathlen;
2788
0
  char user[PR_TUNABLE_LOGIN_MAX+1];
2789
2790
0
  if (path == NULL ||
2791
0
      buf == NULL ||
2792
0
      buflen == 0) {
2793
0
    errno = EINVAL;
2794
0
    return -1;
2795
0
  }
2796
2797
0
  if (path[0] != '~') {
2798
0
    sstrncpy(buf, path, buflen);
2799
0
    return 1;
2800
0
  }
2801
2802
0
  memset(user, '\0', sizeof(user));
2803
2804
  /* The first character of the given path is '~'.
2805
   *
2806
   * We next need to see what the rest of the path looks like.  Could be:
2807
   *
2808
   *  "~"
2809
   *  "~user"
2810
   *  "~/"
2811
   *  "~/path"
2812
   *  "~user/path"
2813
   */
2814
2815
0
  pathlen = strlen(path);
2816
0
  if (pathlen == 1) {
2817
    /* If the path is just "~", AND we're chrooted, then the interpolation
2818
     * is easy.
2819
     */
2820
0
    if (session.chroot_path != NULL) {
2821
0
      sstrncpy(buf, session.chroot_path, buflen);
2822
0
      return 1;
2823
0
    }
2824
2825
    /* If we are not chrooted, but we DO know the home directory of the
2826
     * current user, then interpolation is easy.
2827
     */
2828
0
    if (session.user_homedir != NULL) {
2829
0
      sstrncpy(buf, session.user_homedir, buflen);
2830
0
      return 1;
2831
0
    }
2832
0
  }
2833
2834
0
  ptr = strchr(path, '/');
2835
0
  if (ptr == NULL) {
2836
0
    struct stat st;
2837
2838
    /* No path separator present, which means path must be "~user".
2839
     *
2840
     * This means that a path of "~foo" could be a file with that exact
2841
     * name, or it could be that user's home directory.  Let's find out
2842
     * which it is.
2843
     */
2844
2845
0
    if (pr_fsio_stat(path, &st) < 0) {
2846
       /* Must be a user, if anything...otherwise it's probably a typo.
2847
        *
2848
        * The user name, then, is everything just past the '~' character.
2849
        */
2850
0
      sstrncpy(user, path+1,
2851
0
        pathlen-1 > sizeof(user)-1 ? sizeof(user)-1 : pathlen-1);
2852
2853
0
    } else {
2854
      /* This IS the file in question, perform no interpolation. */
2855
0
      return 0;
2856
0
    }
2857
2858
0
  } else {
2859
0
    currlen = ptr - path;
2860
0
    if (currlen > 1) {
2861
      /* Copy over the username. */
2862
0
      sstrncpy(user, path+1,
2863
0
        currlen > sizeof(user)-1 ? sizeof(user)-1 : currlen);
2864
0
    }
2865
2866
    /* Advance past the '/'. */
2867
0
    ptr++;
2868
0
  }
2869
2870
0
  if (user[0] == '\0') {
2871
    /* No user name provided.  If we are chrooted, we leave it that way.
2872
     * Otherwise, we're not chrooted, and we can assume the current user.
2873
     */
2874
0
    if (session.chroot_path == NULL) {
2875
0
      sstrncpy(user, session.user, sizeof(user)-1);
2876
0
    }
2877
0
  }
2878
2879
0
  if (user[0] != '\0') {
2880
0
    if (session.user != NULL &&
2881
0
        strcmp(user, session.user) == 0 &&
2882
0
        session.user_homedir != NULL) {
2883
0
      sstrncpy(buf, session.user_homedir, buflen);
2884
2885
0
    } else {
2886
0
      struct passwd *pw = NULL;
2887
0
      pool *p = NULL;
2888
2889
      /* We need to look up the info for the given username, and add it
2890
       * into the buffer.
2891
       *
2892
       * The permanent pool is used here, rather than session.pool, as path
2893
       * interpolation can occur during startup parsing, when session.pool does
2894
       * not exist.  It does not really matter, since the allocated sub pool
2895
       * is destroyed shortly.
2896
       */
2897
0
      p = make_sub_pool(permanent_pool);
2898
0
      pr_pool_tag(p, "pr_fs_interpolate() pool");
2899
2900
0
      pw = pr_auth_getpwnam(p, user);
2901
0
      if (pw == NULL) {
2902
0
        destroy_pool(p);
2903
0
        errno = ENOENT;
2904
0
        return -1;
2905
0
      }
2906
2907
0
      sstrncpy(buf, pw->pw_dir, buflen);
2908
2909
      /* Done with pw, which means we can destroy the temporary pool now. */
2910
0
      destroy_pool(p);
2911
0
    }
2912
2913
0
  } else {
2914
    /* We're chrooted. */
2915
0
    sstrncpy(buf, "/", buflen);
2916
0
  }
2917
2918
0
  currlen = strlen(buf);
2919
2920
0
  if (ptr != NULL &&
2921
0
      currlen < buflen &&
2922
0
      buf[currlen-1] != '/') {
2923
0
    buf[currlen++] = '/';
2924
0
  }
2925
2926
0
  if (ptr != NULL) {
2927
0
    sstrncpy(&buf[currlen], ptr, buflen - currlen);
2928
0
  }
2929
2930
0
  return 1;
2931
0
}
2932
2933
0
int pr_fs_resolve_partial(const char *path, char *buf, size_t buflen, int op) {
2934
0
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
2935
0
       workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'},
2936
0
       namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
2937
0
       *where = NULL, *ptr = NULL, *last = NULL;
2938
0
  pr_fs_t *fs = NULL;
2939
0
  int len = 0, fini = 1, link_cnt = 0;
2940
0
  ino_t prev_inode = 0;
2941
0
  dev_t prev_device = 0;
2942
0
  struct stat sbuf;
2943
2944
0
  if (path == NULL ||
2945
0
      buf == NULL ||
2946
0
      buflen == 0) {
2947
0
    errno = EINVAL;
2948
0
    return -1;
2949
0
  }
2950
2951
0
  if (*path != '/') {
2952
0
    if (*path == '~') {
2953
0
      switch (pr_fs_interpolate(path, curpath, sizeof(curpath)-1)) {
2954
0
        case -1:
2955
0
          return -1;
2956
2957
0
        case 0:
2958
0
          sstrncpy(curpath, path, sizeof(curpath));
2959
0
          sstrncpy(workpath, cwd, sizeof(workpath));
2960
0
          break;
2961
0
      }
2962
2963
0
    } else {
2964
0
      sstrncpy(curpath, path, sizeof(curpath));
2965
0
      sstrncpy(workpath, cwd, sizeof(workpath));
2966
0
    }
2967
2968
0
  } else {
2969
0
    sstrncpy(curpath, path, sizeof(curpath));
2970
0
  }
2971
2972
0
  while (fini--) {
2973
0
    where = curpath;
2974
2975
0
    while (*where != '\0') {
2976
0
      pr_signals_handle();
2977
2978
      /* Handle "." */
2979
0
      if (strncmp(where, ".", 2) == 0) {
2980
0
        where++;
2981
0
        continue;
2982
0
      }
2983
2984
      /* Handle ".." */
2985
0
      if (strncmp(where, "..", 3) == 0) {
2986
0
        where += 2;
2987
0
        ptr = last = workpath;
2988
2989
0
        while (*ptr) {
2990
0
          if (*ptr == '/') {
2991
0
            last = ptr;
2992
0
          }
2993
0
          ptr++;
2994
0
        }
2995
2996
0
        *last = '\0';
2997
0
        continue;
2998
0
      }
2999
3000
      /* Handle "./" */
3001
0
      if (strncmp(where, "./", 2) == 0) {
3002
0
        where += 2;
3003
0
        continue;
3004
0
      }
3005
3006
      /* Handle "../" */
3007
0
      if (strncmp(where, "../", 3) == 0) {
3008
0
        where += 3;
3009
0
        ptr = last = workpath;
3010
3011
0
        while (*ptr) {
3012
0
          if (*ptr == '/') {
3013
0
            last = ptr;
3014
0
          }
3015
0
          ptr++;
3016
0
        }
3017
3018
0
        *last = '\0';
3019
0
        continue;
3020
0
      }
3021
3022
0
      ptr = strchr(where, '/');
3023
0
      if (ptr == NULL) {
3024
0
        size_t wherelen = strlen(where);
3025
3026
0
        ptr = where;
3027
0
        if (wherelen >= 1) {
3028
0
          ptr += (wherelen - 1);
3029
0
        }
3030
3031
0
      } else {
3032
0
        *ptr = '\0';
3033
0
      }
3034
3035
0
      sstrncpy(namebuf, workpath, sizeof(namebuf));
3036
3037
0
      if (*namebuf) {
3038
0
        for (last = namebuf; *last; last++) {
3039
0
        }
3040
3041
0
        if (*--last != '/') {
3042
0
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3043
0
        }
3044
3045
0
      } else {
3046
0
        sstrcat(namebuf, "/", sizeof(namebuf)-1);
3047
0
      }
3048
3049
0
      sstrcat(namebuf, where, sizeof(namebuf)-1);
3050
3051
0
      where = ++ptr;
3052
3053
0
      fs = lookup_dir_fs(namebuf, op);
3054
3055
0
      if (fs_cache_lstat(fs, namebuf, &sbuf) == -1) {
3056
0
        return -1;
3057
0
      }
3058
3059
0
      if (S_ISLNK(sbuf.st_mode)) {
3060
0
        char linkpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3061
3062
        /* Detect an obvious recursive symlink */
3063
0
        if (sbuf.st_ino && (ino_t) sbuf.st_ino == prev_inode &&
3064
0
            sbuf.st_dev && (dev_t) sbuf.st_dev == prev_device) {
3065
0
          errno = ELOOP;
3066
0
          return -1;
3067
0
        }
3068
3069
0
        prev_inode = (ino_t) sbuf.st_ino;
3070
0
        prev_device = (dev_t) sbuf.st_dev;
3071
3072
0
        if (++link_cnt > PR_FSIO_MAX_LINK_COUNT) {
3073
0
          errno = ELOOP;
3074
0
          return -1;
3075
0
        }
3076
  
3077
0
        len = pr_fsio_readlink(namebuf, linkpath, sizeof(linkpath)-1);
3078
0
        if (len <= 0) {
3079
0
          errno = ENOENT;
3080
0
          return -1;
3081
0
        }
3082
3083
0
        *(linkpath + len) = '\0';
3084
0
        if (*linkpath == '/') {
3085
0
          *workpath = '\0';
3086
0
        }
3087
3088
        /* Trim any trailing slash. */
3089
0
        if (linkpath[len-1] == '/') {
3090
0
          linkpath[len-1] = '\0';
3091
0
        }
3092
3093
0
        if (*linkpath == '~') {
3094
0
          char tmpbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3095
3096
0
          *workpath = '\0';
3097
0
          sstrncpy(tmpbuf, linkpath, sizeof(tmpbuf));
3098
3099
0
          if (pr_fs_interpolate(tmpbuf, linkpath, sizeof(linkpath)-1) < 0) {
3100
0
      return -1;
3101
0
          }
3102
0
        }
3103
3104
0
        if (*where) {
3105
0
          sstrcat(linkpath, "/", sizeof(linkpath)-1);
3106
0
          sstrcat(linkpath, where, sizeof(linkpath)-1);
3107
0
        }
3108
3109
0
        sstrncpy(curpath, linkpath, sizeof(curpath));
3110
0
        fini++;
3111
0
        break; /* continue main loop */
3112
0
      }
3113
3114
0
      if (S_ISDIR(sbuf.st_mode)) {
3115
0
        sstrncpy(workpath, namebuf, sizeof(workpath));
3116
0
        continue;
3117
0
      }
3118
3119
0
      if (*where) {
3120
0
        errno = ENOENT;
3121
0
        return -1;               /* path/notadir/morepath */
3122
0
      }
3123
3124
0
      sstrncpy(workpath, namebuf, sizeof(workpath));
3125
0
    }
3126
0
  }
3127
3128
0
  if (!workpath[0]) {
3129
0
    sstrncpy(workpath, "/", sizeof(workpath));
3130
0
  }
3131
3132
0
  sstrncpy(buf, workpath, buflen);
3133
0
  return 0;
3134
0
}
3135
3136
0
int pr_fs_resolve_path(const char *path, char *buf, size_t buflen, int op) {
3137
0
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
3138
0
       workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'},
3139
0
       namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
3140
0
       *where = NULL, *ptr = NULL, *last = NULL;
3141
0
  pr_fs_t *fs = NULL;
3142
0
  int len = 0, fini = 1, link_cnt = 0;
3143
0
  ino_t prev_inode = 0;
3144
0
  dev_t prev_device = 0;
3145
0
  struct stat sbuf;
3146
3147
0
  if (path == NULL ||
3148
0
      buf == NULL ||
3149
0
      buflen == 0) {
3150
0
    errno = EINVAL;
3151
0
    return -1;
3152
0
  }
3153
3154
0
  if (pr_fs_interpolate(path, curpath, sizeof(curpath)-1) != -1) {
3155
0
    sstrncpy(curpath, path, sizeof(curpath));
3156
0
  }
3157
3158
0
  if (curpath[0] != '/') {
3159
0
    sstrncpy(workpath, cwd, sizeof(workpath));
3160
3161
0
  } else {
3162
0
    workpath[0] = '\0';
3163
0
  }
3164
3165
0
  while (fini--) {
3166
0
    where = curpath;
3167
3168
0
    while (*where != '\0') {
3169
0
      pr_signals_handle();
3170
3171
0
      if (strncmp(where, ".", 2) == 0) {
3172
0
        where++;
3173
0
        continue;
3174
0
      }
3175
3176
      /* handle "./" */
3177
0
      if (strncmp(where, "./", 2) == 0) {
3178
0
        where += 2;
3179
0
        continue;
3180
0
      }
3181
3182
      /* handle "../" */
3183
0
      if (strncmp(where, "../", 3) == 0) {
3184
0
        where += 3;
3185
0
        ptr = last = workpath;
3186
0
        while (*ptr) {
3187
0
          if (*ptr == '/') {
3188
0
            last = ptr;
3189
0
          }
3190
0
          ptr++;
3191
0
        }
3192
3193
0
        *last = '\0';
3194
0
        continue;
3195
0
      }
3196
3197
0
      ptr = strchr(where, '/');
3198
0
      if (ptr == NULL) {
3199
0
        size_t wherelen = strlen(where);
3200
3201
0
        ptr = where;
3202
0
        if (wherelen >= 1) {
3203
0
          ptr += (wherelen - 1);
3204
0
        }
3205
3206
0
      } else {
3207
0
        *ptr = '\0';
3208
0
      }
3209
3210
0
      sstrncpy(namebuf, workpath, sizeof(namebuf));
3211
3212
0
      if (*namebuf) {
3213
0
        for (last = namebuf; *last; last++) {
3214
0
        }
3215
0
        if (*--last != '/') {
3216
0
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3217
0
        }
3218
3219
0
      } else {
3220
0
        sstrcat(namebuf, "/", sizeof(namebuf)-1);
3221
0
      }
3222
3223
0
      sstrcat(namebuf, where, sizeof(namebuf)-1);
3224
3225
0
      where = ++ptr;
3226
3227
0
      fs = lookup_dir_fs(namebuf, op);
3228
0
      if (fs_cache_lstat(fs, namebuf, &sbuf) == -1) {
3229
0
        errno = ENOENT;
3230
0
        return -1;
3231
0
      }
3232
3233
0
      if (S_ISLNK(sbuf.st_mode)) {
3234
0
        char linkpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3235
3236
        /* Detect an obvious recursive symlink */
3237
0
        if (sbuf.st_ino && (ino_t) sbuf.st_ino == prev_inode &&
3238
0
            sbuf.st_dev && (dev_t) sbuf.st_dev == prev_device) {
3239
0
          errno = ELOOP;
3240
0
          return -1;
3241
0
        }
3242
3243
0
        prev_inode = (ino_t) sbuf.st_ino;
3244
0
        prev_device = (dev_t) sbuf.st_dev;
3245
3246
0
        if (++link_cnt > PR_FSIO_MAX_LINK_COUNT) {
3247
0
          errno = ELOOP;
3248
0
          return -1;
3249
0
        }
3250
3251
0
        len = pr_fsio_readlink(namebuf, linkpath, sizeof(linkpath)-1);
3252
0
        if (len <= 0) {
3253
0
          errno = ENOENT;
3254
0
          return -1;
3255
0
        }
3256
3257
0
        *(linkpath+len) = '\0';
3258
3259
0
        if (*linkpath == '/') {
3260
0
          *workpath = '\0';
3261
0
        }
3262
3263
        /* Trim any trailing slash. */
3264
0
        if (linkpath[len-1] == '/') {
3265
0
          linkpath[len-1] = '\0';
3266
0
        }
3267
3268
0
        if (*linkpath == '~') {
3269
0
          char tmpbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3270
0
          *workpath = '\0';
3271
3272
0
          sstrncpy(tmpbuf, linkpath, sizeof(tmpbuf));
3273
3274
0
          if (pr_fs_interpolate(tmpbuf, linkpath, sizeof(linkpath)-1) < 0) {
3275
0
      return -1;
3276
0
          }
3277
0
        }
3278
3279
0
        if (*where) {
3280
0
          sstrcat(linkpath, "/", sizeof(linkpath)-1);
3281
0
          sstrcat(linkpath, where, sizeof(linkpath)-1);
3282
0
        }
3283
3284
0
        sstrncpy(curpath, linkpath, sizeof(curpath));
3285
0
        fini++;
3286
0
        break; /* continue main loop */
3287
0
      }
3288
3289
0
      if (S_ISDIR(sbuf.st_mode)) {
3290
0
        sstrncpy(workpath, namebuf, sizeof(workpath));
3291
0
        continue;
3292
0
      }
3293
3294
0
      if (*where) {
3295
0
        errno = ENOENT;
3296
0
        return -1;               /* path/notadir/morepath */
3297
0
      }
3298
3299
0
      sstrncpy(workpath, namebuf, sizeof(workpath));
3300
0
    }
3301
0
  }
3302
3303
0
  if (!workpath[0]) {
3304
0
    sstrncpy(workpath, "/", sizeof(workpath));
3305
0
  }
3306
3307
0
  sstrncpy(buf, workpath, buflen);
3308
0
  return 0;
3309
0
}
3310
3311
0
int pr_fs_clean_path2(const char *path, char *buf, size_t buflen, int flags) {
3312
0
  char workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3313
0
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'};
3314
0
  char namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'};
3315
0
  int fini = 1, have_abs_path = FALSE;
3316
3317
0
  if (path == NULL ||
3318
0
      buf == NULL) {
3319
0
    errno = EINVAL;
3320
0
    return -1;
3321
0
  }
3322
3323
0
  if (buflen == 0) {
3324
0
    return 0;
3325
0
  }
3326
3327
0
  sstrncpy(curpath, path, sizeof(curpath));
3328
3329
0
  if (*curpath == '/') {
3330
0
    have_abs_path = TRUE;
3331
0
  }
3332
3333
  /* main loop */
3334
0
  while (fini--) {
3335
0
    char *where = NULL, *ptr = NULL, *last = NULL;
3336
3337
0
    where = curpath;
3338
0
    while (*where != '\0') {
3339
0
      pr_signals_handle();
3340
3341
0
      if (strncmp(where, ".", 2) == 0) {
3342
0
        where++;
3343
0
        continue;
3344
0
      }
3345
3346
      /* handle "./" */
3347
0
      if (strncmp(where, "./", 2) == 0) {
3348
0
        where += 2;
3349
0
        continue;
3350
0
      }
3351
3352
      /* handle ".." */
3353
0
      if (strncmp(where, "..", 3) == 0) {
3354
0
        where += 2;
3355
0
        ptr = last = workpath;
3356
3357
0
        while (*ptr) {
3358
0
          pr_signals_handle();
3359
3360
0
          if (*ptr == '/') {
3361
0
            last = ptr;
3362
0
          }
3363
3364
0
          ptr++;
3365
0
        }
3366
3367
0
        *last = '\0';
3368
0
        continue;
3369
0
      }
3370
3371
      /* handle "../" */
3372
0
      if (strncmp(where, "../", 3) == 0) {
3373
0
        where += 3;
3374
0
        ptr = last = workpath;
3375
3376
0
        while (*ptr) {
3377
0
          pr_signals_handle();
3378
3379
0
          if (*ptr == '/') {
3380
0
            last = ptr;
3381
0
          }
3382
0
          ptr++;
3383
0
        }
3384
3385
0
        *last = '\0';
3386
0
        continue;
3387
0
      }
3388
3389
0
      ptr = strchr(where, '/');
3390
0
      if (ptr == NULL) {
3391
0
        size_t wherelen = strlen(where);
3392
3393
0
        ptr = where;
3394
0
        if (wherelen >= 1) {
3395
0
          ptr += (wherelen - 1);
3396
0
        }
3397
3398
0
      } else {
3399
0
        *ptr = '\0';
3400
0
      }
3401
3402
0
      sstrncpy(namebuf, workpath, sizeof(namebuf));
3403
3404
0
      if (*namebuf) {
3405
0
        for (last = namebuf; *last; last++) {
3406
0
        }
3407
0
        if (*--last != '/') {
3408
0
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3409
0
        }
3410
3411
0
      } else {
3412
0
        if (have_abs_path ||
3413
0
            (flags & PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH)) {
3414
0
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3415
0
          have_abs_path = FALSE;
3416
0
        }
3417
0
      }
3418
3419
0
      sstrcat(namebuf, where, sizeof(namebuf)-1);
3420
0
      namebuf[sizeof(namebuf)-1] = '\0';
3421
3422
0
      where = ++ptr;
3423
3424
0
      sstrncpy(workpath, namebuf, sizeof(workpath));
3425
0
    }
3426
0
  }
3427
3428
0
  if (!workpath[0]) {
3429
0
    sstrncpy(workpath, "/", sizeof(workpath));
3430
0
  }
3431
3432
0
  sstrncpy(buf, workpath, buflen);
3433
0
  return 0;
3434
0
}
3435
3436
0
void pr_fs_clean_path(const char *path, char *buf, size_t buflen) {
3437
0
  pr_fs_clean_path2(path, buf, buflen, PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH);
3438
0
}
3439
3440
0
int pr_fs_use_encoding(int do_encode) {
3441
0
  int curr_setting = use_encoding;
3442
3443
0
  if (do_encode != TRUE &&
3444
0
      do_encode != FALSE) {
3445
0
    errno = EINVAL;
3446
0
    return -1;
3447
0
  }
3448
3449
0
  use_encoding = do_encode;
3450
0
  return curr_setting;
3451
0
}
3452
3453
0
char *pr_fs_decode_path2(pool *p, const char *path, int flags) {
3454
#if defined(PR_USE_NLS)
3455
  size_t outlen;
3456
  char *res;
3457
3458
  if (p == NULL ||
3459
      path == NULL) {
3460
    errno = EINVAL;
3461
    return NULL;
3462
  }
3463
3464
  if (use_encoding == FALSE) {
3465
    return (char *) path;
3466
  }
3467
3468
  res = pr_decode_str(p, path, strlen(path), &outlen);
3469
  if (res == NULL) {
3470
    int xerrno = errno;
3471
3472
    pr_trace_msg("encode", 1, "error decoding path '%s': %s", path,
3473
      strerror(xerrno));
3474
3475
    if (pr_trace_get_level("encode") >= 14) {
3476
      /* Write out the path we tried (and failed) to decode, in hex. */
3477
      register unsigned int i;
3478
      unsigned char *raw_path;
3479
      size_t pathlen, raw_pathlen;
3480
3481
      pathlen = strlen(path);
3482
      raw_pathlen = (pathlen * 8) + 1;
3483
      raw_path = pcalloc(p, raw_pathlen + 1);
3484
3485
      for (i = 0; i < pathlen; i++) {
3486
        pr_snprintf((char *) (raw_path + (i * 8)), (raw_pathlen - 1) - (i * 8),
3487
          "0x%02x ", (unsigned char) path[i]);
3488
      }
3489
3490
      pr_trace_msg("encode", 14, "unable to decode path (raw bytes): %s",
3491
        raw_path);
3492
    }
3493
3494
    if (flags & FSIO_DECODE_FL_TELL_ERRORS) {
3495
      unsigned long policy;
3496
3497
      policy = pr_encode_get_policy();
3498
      if (policy & PR_ENCODE_POLICY_FL_REQUIRE_VALID_ENCODING) {
3499
        /* Note: At present, we DO return null here to callers, to indicate
3500
         * the illegal encoding (Bug#4125), if configured to do so via
3501
         * e.g. the RequireValidEncoding LangOption.
3502
         */
3503
        errno = xerrno;
3504
        return NULL;
3505
      }
3506
    }
3507
3508
    return (char *) path;
3509
  }
3510
3511
  pr_trace_msg("encode", 5, "decoded '%s' into '%s'", path, res);
3512
  return res;
3513
#else
3514
0
  if (p == NULL ||
3515
0
      path == NULL) {
3516
0
    errno = EINVAL;
3517
0
    return NULL;
3518
0
  }
3519
3520
0
  return (char *) path;
3521
0
#endif /* PR_USE_NLS */
3522
0
}
3523
3524
0
char *pr_fs_decode_path(pool *p, const char *path) {
3525
0
  return pr_fs_decode_path2(p, path, 0);
3526
0
}
3527
3528
0
char *pr_fs_encode_path(pool *p, const char *path) {
3529
#ifdef PR_USE_NLS
3530
  size_t outlen;
3531
  char *res;
3532
3533
  if (p == NULL ||
3534
      path == NULL) {
3535
    errno = EINVAL;
3536
    return NULL;
3537
  }
3538
3539
  if (!use_encoding) {
3540
    return (char *) path;
3541
  }
3542
3543
  res = pr_encode_str(p, path, strlen(path), &outlen);
3544
  if (res == NULL) {
3545
    int xerrno = errno;
3546
3547
    pr_trace_msg("encode", 1, "error encoding path '%s': %s", path,
3548
      strerror(xerrno));
3549
3550
    if (pr_trace_get_level("encode") >= 14) {
3551
      /* Write out the path we tried (and failed) to encode, in hex. */
3552
      register unsigned int i;
3553
      unsigned char *raw_path;
3554
      size_t pathlen, raw_pathlen;
3555
3556
      pathlen = strlen(path);
3557
      raw_pathlen = (pathlen * 8) + 1;
3558
      raw_path = pcalloc(p, raw_pathlen + 1);
3559
3560
      for (i = 0; i < pathlen; i++) {
3561
        pr_snprintf((char *) (raw_path + (i * 8)), (raw_pathlen - 1) - (i * 8),
3562
          "0x%02x ", (unsigned char) path[i]);
3563
      }
3564
3565
      pr_trace_msg("encode", 14, "unable to encode path (raw bytes): %s",
3566
        raw_path);
3567
    }
3568
3569
    /* Note: At present, we do NOT return null here to callers; we assume
3570
     * that all local names, being encoded for the remote client, are OK.
3571
     * Revisit this assumption if necessary (Bug#4125).
3572
     */
3573
3574
    return (char *) path;
3575
  }
3576
3577
  pr_trace_msg("encode", 5, "encoded '%s' into '%s'", path, res);
3578
  return res;
3579
#else
3580
0
  if (p == NULL ||
3581
0
      path == NULL) {
3582
0
    errno = EINVAL;
3583
0
    return NULL;
3584
0
  }
3585
3586
0
  return (char *) path;
3587
0
#endif /* PR_USE_NLS */
3588
0
}
3589
3590
0
array_header *pr_fs_split_path(pool *p, const char *path) {
3591
0
  int res, have_abs_path = FALSE;
3592
0
  char *buf;
3593
0
  size_t buflen, bufsz, pathlen;
3594
0
  array_header *components;
3595
3596
0
  if (p == NULL ||
3597
0
      path == NULL) {
3598
0
    errno = EINVAL;
3599
0
    return NULL;
3600
0
  }
3601
3602
0
  pathlen = strlen(path);
3603
0
  if (pathlen == 0) {
3604
0
    errno = EINVAL;
3605
0
    return NULL;
3606
0
  }
3607
3608
0
  if (*path == '/') {
3609
0
    have_abs_path = TRUE;
3610
0
  }
3611
3612
  /* Clean the path first */
3613
0
  bufsz = PR_TUNABLE_PATH_MAX;
3614
0
  buf = pcalloc(p, bufsz + 1);
3615
3616
0
  res = pr_fs_clean_path2(path, buf, bufsz,
3617
0
    PR_FSIO_CLEAN_PATH_FL_MAKE_ABS_PATH);
3618
0
  if (res < 0) {
3619
0
    int xerrno = errno;
3620
3621
0
    pr_trace_msg(trace_channel, 7, "error cleaning path '%s': %s", path,
3622
0
      strerror(xerrno));
3623
0
    errno = xerrno;
3624
0
    return NULL;
3625
0
  }
3626
3627
0
  pr_trace_msg(trace_channel, 18, "splitting cleaned path '%s' (was '%s')",
3628
0
    buf, path);
3629
3630
  /* Special-case handling of just "/", since pr_str_text_to_array() will
3631
   * "eat" that delimiter.
3632
   */
3633
0
  buflen = strlen(buf);
3634
0
  if (buflen == 1 &&
3635
0
      buf[0] == '/') {
3636
0
    pr_trace_msg(trace_channel, 18, "split path '%s' into 1 component", path);
3637
3638
0
    components = make_array(p, 1, sizeof(char *));
3639
0
    *((char **) push_array(components)) = pstrdup(p, "/");
3640
3641
0
    return components;
3642
0
  }
3643
3644
0
  components = pr_str_text_to_array(p, buf, '/');
3645
0
  if (components != NULL) {
3646
0
    pr_trace_msg(trace_channel, 17, "split path '%s' into %u %s", path,
3647
0
      components->nelts, components->nelts != 1 ? "components" : "component");
3648
3649
0
    if (pr_trace_get_level(trace_channel) >= 18) {
3650
0
      register unsigned int i;
3651
3652
0
      for (i = 0; i < components->nelts; i++) {
3653
0
        char *component;
3654
3655
0
        component = ((char **) components->elts)[i];
3656
0
        if (component == NULL) {
3657
0
          component = "NULL";
3658
0
        }
3659
3660
0
        pr_trace_msg(trace_channel, 18, "path '%s' component #%u: '%s'",
3661
0
          path, i + 1, component);
3662
0
      }
3663
0
    }
3664
0
  }
3665
3666
0
  if (have_abs_path == TRUE) {
3667
0
    array_header *root_component;
3668
3669
    /* Since pr_str_text_to_array() will treat the leading '/' as a delimiter,
3670
     * it will be stripped and not included as a path component.  But it
3671
     * DOES need to be there.
3672
     */
3673
0
    root_component = make_array(p, 1, sizeof(char *));
3674
0
    *((char **) push_array(root_component)) = pstrdup(p, "/");
3675
3676
0
    array_cat(root_component, components);
3677
0
    components = root_component;
3678
0
  }
3679
3680
0
  return components;
3681
0
}
3682
3683
0
char *pr_fs_join_path(pool *p, array_header *components, size_t count) {
3684
0
  register unsigned int i;
3685
0
  char *path = NULL;
3686
3687
0
  if (p == NULL ||
3688
0
      components == NULL ||
3689
0
      components->nelts == 0 ||
3690
0
      count == 0) {
3691
0
    errno = EINVAL;
3692
0
    return NULL;
3693
0
  }
3694
3695
  /* Can't join more components than we have. */
3696
0
  if (count > components->nelts) {
3697
0
    errno = EINVAL;
3698
0
    return NULL;
3699
0
  }
3700
3701
0
  path = ((char **) components->elts)[0];
3702
0
  path = pstrdup(p, path);
3703
3704
0
  for (i = 1; i < count; i++) {
3705
0
    char *elt;
3706
3707
0
    elt = ((char **) components->elts)[i];
3708
0
    path = pdircat(p, path, elt, NULL);
3709
0
  }
3710
3711
0
  return path;
3712
0
}
3713
3714
/* This function checks the given path's prefix against the paths that
3715
 * have been registered.  If no matching path prefix has been registered,
3716
 * the path is considered invalid.
3717
 */
3718
0
int pr_fs_valid_path(const char *path) {
3719
0
  if (path == NULL) {
3720
0
    errno = EINVAL;
3721
0
    return -1;
3722
0
  }
3723
3724
0
  if (fs_map != NULL &&
3725
0
      fs_map->nelts > 0) {
3726
0
    pr_fs_t *fsi = NULL, **fs_objs = (pr_fs_t **) fs_map->elts;
3727
0
    register unsigned int i;
3728
3729
0
    for (i = 0; i < fs_map->nelts; i++) {
3730
0
      fsi = fs_objs[i];
3731
3732
0
      if (strncmp(fsi->fs_path, path, strlen(fsi->fs_path)) == 0) {
3733
0
        return 0;
3734
0
      }
3735
0
    }
3736
0
  }
3737
3738
  /* Also check the path against the default '/' path. */
3739
0
  if (*path == '/') {
3740
0
    return 0;
3741
0
  }
3742
3743
0
  errno = ENOENT;
3744
0
  return -1;
3745
0
}
3746
3747
0
void pr_fs_virtual_path(const char *path, char *buf, size_t buflen) {
3748
0
  char curpath[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
3749
0
       workpath[PR_TUNABLE_PATH_MAX + 1] = {'\0'},
3750
0
       namebuf[PR_TUNABLE_PATH_MAX + 1]  = {'\0'},
3751
0
       *where = NULL, *ptr = NULL, *last = NULL;
3752
0
  int fini = 1;
3753
3754
0
  if (path == NULL) {
3755
0
    return;
3756
0
  }
3757
3758
0
  if (pr_fs_interpolate(path, curpath, sizeof(curpath)-1) != -1) {
3759
0
    sstrncpy(curpath, path, sizeof(curpath));
3760
0
  }
3761
3762
0
  if (curpath[0] != '/') {
3763
0
    sstrncpy(workpath, vwd, sizeof(workpath));
3764
3765
0
  } else {
3766
0
    workpath[0] = '\0';
3767
0
  }
3768
3769
  /* curpath is path resolving */
3770
  /* linkpath is path a symlink pointed to */
3771
  /* workpath is the path we've resolved */
3772
3773
  /* main loop */
3774
0
  while (fini--) {
3775
0
    where = curpath;
3776
3777
0
    while (*where != '\0') {
3778
0
      pr_signals_handle();
3779
3780
0
      if (strncmp(where, ".", 2) == 0) {
3781
0
        where++;
3782
0
        continue;
3783
0
      }
3784
3785
      /* handle "./" */
3786
0
      if (strncmp(where, "./", 2) == 0) {
3787
0
        where += 2;
3788
0
        continue;
3789
0
      }
3790
3791
      /* handle ".." */
3792
0
      if (strncmp(where, "..", 3) == 0) {
3793
0
        where += 2;
3794
0
        ptr = last = workpath;
3795
0
        while (*ptr) {
3796
0
          if (*ptr == '/') {
3797
0
            last = ptr;
3798
0
          }
3799
0
          ptr++;
3800
0
        }
3801
3802
0
        *last = '\0';
3803
0
        continue;
3804
0
      }
3805
3806
      /* handle "../" */
3807
0
      if (strncmp(where, "../", 3) == 0) {
3808
0
        where += 3;
3809
0
        ptr = last = workpath;
3810
0
        while (*ptr) {
3811
0
          if (*ptr == '/') {
3812
0
            last = ptr;
3813
0
          }
3814
0
          ptr++;
3815
0
        }
3816
3817
0
        *last = '\0';
3818
0
        continue;
3819
0
      }
3820
3821
0
      ptr = strchr(where, '/');
3822
0
      if (ptr == NULL) {
3823
0
        size_t wherelen = strlen(where);
3824
3825
0
        ptr = where;
3826
0
        if (wherelen >= 1) {
3827
0
          ptr += (wherelen - 1);
3828
0
        }
3829
3830
0
      } else {
3831
0
        *ptr = '\0';
3832
0
      }
3833
3834
0
      sstrncpy(namebuf, workpath, sizeof(namebuf));
3835
3836
0
      if (*namebuf) {
3837
0
        for (last = namebuf; *last; last++) {
3838
0
        }
3839
0
        if (*--last != '/') {
3840
0
          sstrcat(namebuf, "/", sizeof(namebuf)-1);
3841
0
        }
3842
3843
0
      } else {
3844
0
        sstrcat(namebuf, "/", sizeof(namebuf)-1);
3845
0
      }
3846
3847
0
      sstrcat(namebuf, where, sizeof(namebuf)-1);
3848
3849
0
      where = ++ptr;
3850
3851
0
      sstrncpy(workpath, namebuf, sizeof(workpath));
3852
0
    }
3853
0
  }
3854
3855
0
  if (!workpath[0]) {
3856
0
    sstrncpy(workpath, "/", sizeof(workpath));
3857
0
  }
3858
3859
0
  sstrncpy(buf, workpath, buflen);
3860
0
}
3861
3862
0
int pr_fsio_chdir_canon(const char *path, int hidesymlink) {
3863
0
  char resbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3864
0
  pr_fs_t *fs = NULL;
3865
0
  int res = 0;
3866
3867
0
  if (path == NULL) {
3868
0
    errno = EINVAL;
3869
0
    return -1;
3870
0
  }
3871
3872
0
  if (pr_fs_resolve_partial(path, resbuf, sizeof(resbuf)-1,
3873
0
      FSIO_DIR_CHDIR) < 0) {
3874
0
    return -1;
3875
0
  }
3876
3877
0
  fs = lookup_dir_fs(resbuf, FSIO_DIR_CHDIR);
3878
0
  if (fs == NULL) {
3879
0
    return -1;
3880
0
  }
3881
3882
  /* Find the first non-NULL custom chdir handler.  If there are none,
3883
   * use the system chdir.
3884
   */
3885
0
  while (fs && fs->fs_next && !fs->chdir) {
3886
0
    fs = fs->fs_next;
3887
0
  }
3888
3889
0
  pr_trace_msg(trace_channel, 8, "using %s chdir() for path '%s'", fs->fs_name,
3890
0
    path);
3891
0
  res = (fs->chdir)(fs, resbuf);
3892
0
  if (res == 0) {
3893
    /* chdir succeeded, so we set fs_cwd for future references. */
3894
0
     fs_cwd = fs;
3895
3896
0
     if (hidesymlink) {
3897
0
       pr_fs_virtual_path(path, vwd, sizeof(vwd)-1);
3898
3899
0
     } else {
3900
0
       sstrncpy(vwd, resbuf, sizeof(vwd));
3901
0
     }
3902
0
  }
3903
3904
0
  return res;
3905
0
}
3906
3907
0
int pr_fsio_chdir(const char *path, int hidesymlink) {
3908
0
  char resbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3909
0
  pr_fs_t *fs = NULL;
3910
0
  int res;
3911
3912
0
  if (path == NULL) {
3913
0
    errno = EINVAL;
3914
0
    return -1;
3915
0
  }
3916
3917
0
  pr_fs_clean_path(path, resbuf, sizeof(resbuf)-1);
3918
3919
0
  fs = lookup_dir_fs(path, FSIO_DIR_CHDIR);
3920
0
  if (fs == NULL) {
3921
0
    return -1;
3922
0
  }
3923
3924
  /* Find the first non-NULL custom chdir handler.  If there are none,
3925
   * use the system chdir.
3926
   */
3927
0
  while (fs && fs->fs_next && !fs->chdir) {
3928
0
    fs = fs->fs_next;
3929
0
  }
3930
3931
0
  pr_trace_msg(trace_channel, 8, "using %s chdir() for path '%s'", fs->fs_name,
3932
0
    path);
3933
0
  res = (fs->chdir)(fs, resbuf);
3934
0
  if (res == 0) {
3935
    /* chdir succeeded, so we set fs_cwd for future references. */
3936
0
    fs_cwd = fs;
3937
3938
0
    if (hidesymlink) {
3939
0
      pr_fs_virtual_path(path, vwd, sizeof(vwd)-1);
3940
3941
0
    } else {
3942
0
      sstrncpy(vwd, resbuf, sizeof(vwd));
3943
0
    }
3944
0
  }
3945
3946
0
  return res;
3947
0
}
3948
3949
/* fs_opendir, fs_closedir and fs_readdir all use a nifty
3950
 * optimization, caching the last-recently-used pr_fs_t, and
3951
 * avoid future pr_fs_t lookups when iterating via readdir.
3952
 */
3953
0
void *pr_fsio_opendir(const char *path) {
3954
0
  pr_fs_t *fs = NULL;
3955
0
  fsopendir_t *fsod = NULL, *fsodi = NULL;
3956
0
  pool *fsod_pool = NULL;
3957
0
  DIR *res = NULL;
3958
3959
0
  if (path == NULL) {
3960
0
    errno = EINVAL;
3961
0
    return NULL;
3962
0
  }
3963
3964
0
  if (strchr(path, '/') == NULL) {
3965
0
    pr_fs_setcwd(pr_fs_getcwd());
3966
0
    fs = fs_cwd;
3967
3968
0
  } else {
3969
0
    char buf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
3970
3971
0
    if (pr_fs_resolve_partial(path, buf, sizeof(buf)-1, FSIO_DIR_OPENDIR) < 0) {
3972
0
      return NULL;
3973
0
    }
3974
3975
0
    fs = lookup_dir_fs(buf, FSIO_DIR_OPENDIR);
3976
0
  }
3977
3978
  /* Find the first non-NULL custom opendir handler.  If there are none,
3979
   * use the system opendir.
3980
   */
3981
0
  while (fs && fs->fs_next && !fs->opendir) {
3982
0
    fs = fs->fs_next;
3983
0
  }
3984
3985
0
  pr_trace_msg(trace_channel, 8, "using %s opendir() for path '%s'",
3986
0
    fs->fs_name, path);
3987
0
  res = (fs->opendir)(fs, path);
3988
0
  if (res == NULL) {
3989
0
    return NULL;
3990
0
  }
3991
3992
  /* Cache it here */
3993
0
  fs_cache_dir = res;
3994
0
  fs_cache_fsdir = fs;
3995
3996
0
  fsod_pool = make_sub_pool(permanent_pool);
3997
0
  pr_pool_tag(fsod_pool, "fsod subpool");
3998
3999
0
  fsod = pcalloc(fsod_pool, sizeof(fsopendir_t));
4000
0
  if (fsod == NULL) {
4001
0
    if (fs->closedir) {
4002
0
      (fs->closedir)(fs, res);
4003
0
      errno = ENOMEM;
4004
0
      return NULL;
4005
0
    }
4006
4007
0
    sys_closedir(fs, res);
4008
0
    errno = ENOMEM;
4009
0
    return NULL;
4010
0
  }
4011
4012
0
  fsod->pool = fsod_pool;
4013
0
  fsod->dir = res;
4014
0
  fsod->fsdir = fs;
4015
0
  fsod->next = NULL;
4016
0
  fsod->prev = NULL;
4017
4018
0
  if (fsopendir_list) {
4019
4020
    /* find the end of the fsopendir list */
4021
0
    fsodi = fsopendir_list;
4022
0
    while (fsodi->next) {
4023
0
      pr_signals_handle();
4024
0
      fsodi = fsodi->next;
4025
0
    }
4026
4027
0
    fsod->next = NULL;
4028
0
    fsod->prev = fsodi;
4029
0
    fsodi->next = fsod;
4030
4031
0
  } else {
4032
    /* This fsopendir _becomes_ the start of the fsopendir list */
4033
0
    fsopendir_list = fsod;
4034
0
  }
4035
4036
0
  return res;
4037
0
}
4038
4039
0
static pr_fs_t *find_opendir(void *dir, int closing) {
4040
0
  pr_fs_t *fs = NULL;
4041
4042
0
  if (fsopendir_list != NULL) {
4043
0
    fsopendir_t *fsod;
4044
4045
0
    for (fsod = fsopendir_list; fsod; fsod = fsod->next) {
4046
0
      if (fsod->dir != NULL &&
4047
0
          fsod->dir == dir) {
4048
0
        fs = fsod->fsdir;
4049
0
        break;
4050
0
      }
4051
0
    }
4052
4053
0
    if (closing && fsod) {
4054
0
      if (fsod->prev != NULL) {
4055
0
        fsod->prev->next = fsod->next;
4056
0
      }
4057
4058
0
      if (fsod->next != NULL) {
4059
0
        fsod->next->prev = fsod->prev;
4060
0
      }
4061
4062
0
      if (fsod == fsopendir_list) {
4063
0
        fsopendir_list = fsod->next;
4064
0
      }
4065
4066
0
      destroy_pool(fsod->pool);
4067
0
    }
4068
0
  }
4069
4070
0
  if (dir == fs_cache_dir) {
4071
0
    fs = fs_cache_fsdir;
4072
4073
0
    if (closing) {
4074
0
      fs_cache_dir = NULL;
4075
0
      fs_cache_fsdir = NULL;
4076
0
    }
4077
0
  }
4078
4079
0
  if (fs == NULL) {
4080
0
    errno = ENOTDIR;
4081
0
  }
4082
4083
0
  return fs;
4084
0
}
4085
4086
0
int pr_fsio_closedir(void *dir) {
4087
0
  int res;
4088
0
  pr_fs_t *fs;
4089
4090
0
  if (dir == NULL) {
4091
0
    errno = EINVAL;
4092
0
    return -1;
4093
0
  }
4094
4095
0
  fs = find_opendir(dir, TRUE);
4096
0
  if (fs == NULL) {
4097
0
    return -1;
4098
0
  }
4099
4100
  /* Find the first non-NULL custom closedir handler.  If there are none,
4101
   * use the system closedir.
4102
   */
4103
0
  while (fs && fs->fs_next && !fs->closedir) {
4104
0
    fs = fs->fs_next;
4105
0
  }
4106
4107
0
  pr_trace_msg(trace_channel, 8, "using %s closedir()", fs->fs_name);
4108
0
  res = (fs->closedir)(fs, dir);
4109
4110
0
  return res;
4111
0
}
4112
4113
0
struct dirent *pr_fsio_readdir(void *dir) {
4114
0
  struct dirent *res;
4115
0
  pr_fs_t *fs;
4116
4117
0
  if (dir == NULL) {
4118
0
    errno = EINVAL;
4119
0
    return NULL;
4120
0
  }
4121
4122
0
  fs = find_opendir(dir, FALSE);
4123
0
  if (fs == NULL) {
4124
0
    return NULL;
4125
0
  }
4126
4127
  /* Find the first non-NULL custom readdir handler.  If there are none,
4128
   * use the system readdir.
4129
   */
4130
0
  while (fs && fs->fs_next && !fs->readdir) {
4131
0
    fs = fs->fs_next;
4132
0
  }
4133
4134
0
  pr_trace_msg(trace_channel, 8, "using %s readdir()", fs->fs_name);
4135
0
  res = (fs->readdir)(fs, dir);
4136
4137
0
  return res;
4138
0
}
4139
4140
0
int pr_fsio_mkdir(const char *path, mode_t mode) {
4141
0
  int res, xerrno;
4142
0
  pr_fs_t *fs;
4143
0
  mode_t dir_umask = -1, prev_umask = -1, *umask_ptr = NULL;
4144
4145
0
  if (path == NULL) {
4146
0
    errno = EINVAL;
4147
0
    return -1;
4148
0
  }
4149
4150
0
  fs = lookup_dir_fs(path, FSIO_DIR_MKDIR);
4151
0
  if (fs == NULL) {
4152
0
    return -1;
4153
0
  }
4154
4155
  /* Find the first non-NULL custom mkdir handler.  If there are none,
4156
   * use the system mkdir.
4157
   */
4158
0
  while (fs && fs->fs_next && !fs->mkdir) {
4159
0
    fs = fs->fs_next;
4160
0
  }
4161
4162
  /* Make sure we honor the directory Umask, if any (Bug#4311). */
4163
0
  umask_ptr = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
4164
0
  if (umask_ptr == NULL) {
4165
    /* If Umask was configured with a single parameter, then DirUmask
4166
     * would not be present; we still should check for Umask.
4167
     */
4168
0
    umask_ptr = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
4169
0
  }
4170
4171
0
  if (umask_ptr != NULL) {
4172
0
    dir_umask = *umask_ptr;
4173
4174
0
    if (dir_umask != (mode_t) -1) {
4175
0
      prev_umask = umask(dir_umask);
4176
0
    }
4177
0
  }
4178
4179
0
  pr_trace_msg(trace_channel, 8, "using %s mkdir() for path '%s'", fs->fs_name,
4180
0
    path);
4181
0
  res = (fs->mkdir)(fs, path, mode);
4182
0
  xerrno = errno;
4183
4184
0
  if (res == 0 || xerrno == EEXIST) {
4185
0
    pr_fs_clear_cache2(path);
4186
0
  }
4187
4188
0
  if (dir_umask != (mode_t) -1) {
4189
0
    (void) umask(prev_umask);
4190
0
  }
4191
4192
0
  errno = xerrno;
4193
0
  return res;
4194
0
}
4195
4196
int pr_fsio_mkdir_with_error(pool *p, const char *path, mode_t mode,
4197
0
    pr_error_t **err) {
4198
0
  int res;
4199
4200
0
  res = pr_fsio_mkdir(path, mode);
4201
0
  if (res < 0) {
4202
0
    int xerrno = errno;
4203
4204
0
    if (p != NULL &&
4205
0
        err != NULL) {
4206
0
      *err = pr_error_create(p, xerrno);
4207
0
      if (pr_error_explain_mkdir(*err, path, mode) < 0) {
4208
0
        pr_error_destroy(*err);
4209
0
        *err = NULL;
4210
0
      }
4211
0
    }
4212
4213
0
    errno = xerrno;
4214
0
  }
4215
4216
0
  return res;
4217
0
}
4218
4219
0
int pr_fsio_guard_chroot(int guard) {
4220
0
  int prev;
4221
4222
0
  prev = fsio_guard_chroot;
4223
0
  fsio_guard_chroot = guard;
4224
4225
0
  return prev;
4226
0
}
4227
4228
0
unsigned long pr_fsio_set_options(unsigned long opts) {
4229
0
  unsigned long prev;
4230
4231
0
  prev = fsio_opts;
4232
0
  fsio_opts = opts;
4233
4234
0
  return prev;
4235
0
}
4236
4237
0
int pr_fsio_set_use_mkdtemp(int value) {
4238
0
  int prev_value;
4239
4240
0
  if (value != TRUE &&
4241
0
      value != FALSE) {
4242
0
    errno = EINVAL;
4243
0
    return -1;
4244
0
  }
4245
4246
0
  prev_value = fsio_use_mkdtemp;
4247
4248
0
#ifdef HAVE_MKDTEMP
4249
0
  fsio_use_mkdtemp = value;
4250
0
#endif /* HAVE_MKDTEMP */
4251
4252
0
  return prev_value;
4253
0
}
4254
4255
/* Directory-specific "safe" chmod(2) which attempts to avoid/mitigate
4256
 * symlink attacks.
4257
 *
4258
 * To do this, we first open a file descriptor on the given path, using
4259
 * O_NOFOLLOW to avoid symlinks.  If the fd is not to a directory, it's
4260
 * an error.  Then we use fchmod(2) to set the perms.  There is still a
4261
 * race condition here, between the time the directory is created and
4262
 * when we call open(2).  But hopefully the ensuing checks on the fd
4263
 * (i.e. that it IS a directory) can mitigate that race.
4264
 *
4265
 * The fun part is ensuring that the OS/filesystem will give us an fd
4266
 * on a directory path (using O_RDONLY to avoid getting an EISDIR error),
4267
 * whilst being able to do a write (effectively) on the fd by changing
4268
 * its permissions.
4269
 */
4270
0
static int schmod_dir(pool *p, const char *path, mode_t perms, int use_root) {
4271
0
  int flags, fd, ignore_eacces = FALSE, ignore_eperm = FALSE, res, xerrno = 0;
4272
0
  struct stat st;
4273
0
  mode_t dir_mode;
4274
4275
  /* We're not using the pool at the moment. */
4276
0
  (void) p;
4277
4278
  /* Open an fd on the path using O_RDONLY|O_NOFOLLOW, so that we a)
4279
   * avoid symlinks, and b) get an fd on the (hopefully) directory.
4280
   */
4281
0
  flags = O_RDONLY;
4282
0
#ifdef O_NOFOLLOW
4283
0
  flags |= O_NOFOLLOW;
4284
0
#endif
4285
0
  fd = open(path, flags);
4286
0
  xerrno = errno;
4287
4288
0
  if (fd < 0) {
4289
0
    pr_trace_msg(trace_channel, 3,
4290
0
      "schmod: unable to open path '%s': %s", path, strerror(xerrno));
4291
0
    errno = xerrno;
4292
0
    return -1;
4293
0
  }
4294
4295
0
  res = fstat(fd, &st);
4296
0
  if (res < 0) {
4297
0
    xerrno = errno;
4298
4299
0
    (void) close(fd);
4300
4301
0
    pr_trace_msg(trace_channel, 3,
4302
0
      "schmod: unable to fstat path '%s': %s", path, strerror(xerrno));
4303
0
    errno = xerrno;
4304
0
    return -1;
4305
0
  }
4306
4307
  /* We expect only directories. */
4308
0
  if (!S_ISDIR(st.st_mode)) {
4309
0
    xerrno = ENOTDIR;
4310
4311
0
    (void) close(fd);
4312
4313
0
    pr_trace_msg(trace_channel, 3,
4314
0
      "schmod: unable to use path '%s': %s", path, strerror(xerrno));
4315
4316
    /* This is such an unexpected (and possibly malicious) situation that
4317
     * it warrants louder logging.
4318
     */
4319
0
    pr_log_pri(PR_LOG_WARNING,
4320
0
      "WARNING: detected non-directory '%s' during directory creation: "
4321
0
      "possible symlink attack", path);
4322
4323
0
    errno = xerrno;
4324
0
    return -1;
4325
0
  }
4326
4327
  /* Note that some filesystems (e.g. CIFS) may not actually create a
4328
   * directory with the expected 0700 mode.  If that is the case, then a
4329
   * subsequent chmod(2) on that directory will likely fail.  Thus we also
4330
   * double-check the mode of the directory created via mkdtemp(3), and
4331
   * attempt to mitigate Bug#4063.
4332
   */
4333
0
  dir_mode = (st.st_mode & ~S_IFMT);
4334
0
  if (dir_mode != 0700) {
4335
0
    ignore_eacces = ignore_eperm = TRUE;
4336
4337
0
    pr_trace_msg(trace_channel, 3,
4338
0
      "schmod: path '%s' has mode %04o, expected 0700", path, dir_mode);
4339
4340
    /* This is such an unexpected situation that it warrants some logging. */
4341
0
    pr_log_pri(PR_LOG_DEBUG,
4342
0
      "NOTICE: directory '%s' has unexpected mode %04o (expected 0700)", path,
4343
0
      dir_mode);
4344
0
  }
4345
4346
0
  if (use_root) {
4347
0
    PRIVS_ROOT
4348
0
  }
4349
4350
0
  res = fchmod(fd, perms);
4351
0
  xerrno = errno;
4352
4353
  /* Using fchmod(2) on a directory descriptor is not really kosher
4354
   * behavior, but appears to work on most filesystems.  Still, if we
4355
   * get an ENOENT back (as seen on some CIFS mounts, per Bug#4134), try
4356
   * using chmod(2) on the path.
4357
   */
4358
0
  if (res < 0 &&
4359
0
      xerrno == ENOENT) {
4360
0
    ignore_eacces = TRUE;
4361
0
    res = chmod(path, perms);
4362
0
    xerrno = errno;
4363
0
  }
4364
4365
0
  if (use_root) {
4366
0
    PRIVS_RELINQUISH
4367
0
  }
4368
4369
  /* At this point, succeed or fail, we're done with the fd. */
4370
0
  (void) close(fd);
4371
4372
0
  if (res < 0) {
4373
    /* Note: Some filesystem implementations, particularly via FUSE,
4374
     * may not actually implement ownership/permissions (e.g. FAT-based
4375
     * filesystems).  In such cases, chmod(2) et al will return ENOSYS
4376
     * (see Bug#3986).
4377
     *
4378
     * Other filesystem implementations (e.g. CIFS, depending on the mount
4379
     * options) will a chmod(2) that returns ENOENT (see Bug#4134).
4380
     *
4381
     * Should this fail the entire operation?  I'm of two minds about this.
4382
     * On the one hand, such filesystem behavior can undermine wider site
4383
     * security policies; on the other, prohibiting a MKD/MKDIR operation
4384
     * on such filesystems, deliberately used by the site admin, is not
4385
     * useful/friendly behavior.
4386
     *
4387
     * Maybe these exceptions for ENOSYS/ENOENT here should be made
4388
     * configurable?
4389
     */
4390
4391
0
    if (xerrno == ENOSYS ||
4392
0
        xerrno == ENOENT ||
4393
0
        (xerrno == EACCES && ignore_eacces == TRUE) ||
4394
0
        (xerrno == EPERM && ignore_eperm == TRUE)) {
4395
0
      pr_log_debug(DEBUG0, "schmod: unable to set perms %04o on "
4396
0
        "path '%s': %s (chmod(2) not supported by underlying filesystem?)",
4397
0
        perms, path, strerror(xerrno));
4398
0
      return 0;
4399
0
    }
4400
4401
0
    pr_trace_msg(trace_channel, 3,
4402
0
      "schmod: unable to set perms %04o on path '%s': %s", perms, path,
4403
0
      strerror(xerrno));
4404
0
    errno = xerrno;
4405
0
    return -1;
4406
0
  }
4407
4408
0
  return 0;
4409
0
}
4410
4411
/* "safe mkdir" variant of mkdir(2), uses mkdtemp(3), lchown(2), and
4412
 * rename(2) to create a directory which cannot be hijacked by a symlink
4413
 * race (hopefully) before the UserOwner/GroupOwner ownership changes are
4414
 * applied.
4415
 */
4416
int pr_fsio_smkdir(pool *p, const char *path, mode_t mode, uid_t uid,
4417
0
    gid_t gid) {
4418
0
  int res, set_sgid = FALSE, use_mkdtemp, use_root_chown = FALSE, xerrno = 0;
4419
0
  char *tmpl_path;
4420
0
  char *dst_dir, *tmpl;
4421
0
  size_t dst_dirlen, tmpl_len;
4422
4423
0
  if (p == NULL ||
4424
0
      path == NULL) {
4425
0
    errno = EINVAL;
4426
0
    return -1;
4427
0
  }
4428
4429
0
  pr_trace_msg(trace_channel, 9,
4430
0
    "smkdir: path '%s', mode %04o, UID %s, GID %s", path, (unsigned int) mode,
4431
0
    pr_uid2str(p, uid), pr_gid2str(p, gid));
4432
4433
0
  if (fsio_guard_chroot) {
4434
0
    res = chroot_allow_path(path);
4435
0
    if (res < 0) {
4436
0
      return -1;
4437
0
    }
4438
0
  }
4439
4440
0
  use_mkdtemp = fsio_use_mkdtemp;
4441
0
  if (use_mkdtemp == TRUE) {
4442
4443
    /* Note that using mkdtemp(3) is a way of dealing with Bug#3841.  The
4444
     * problem in question, though, only applies if root privs are used
4445
     * to set the ownership.  Thus if root privs are NOT needed, then there
4446
     * is no need to use mkdtemp(3).
4447
     */
4448
4449
0
    if (uid != (uid_t) -1) {
4450
0
      use_root_chown = TRUE;
4451
4452
0
    } else if (gid != (gid_t) -1) {
4453
0
      register unsigned int i;
4454
4455
0
      use_root_chown = TRUE;
4456
4457
      /* Check if session.fsgid is in session.gids.  If not, use root privs.  */
4458
0
      for (i = 0; i < session.gids->nelts; i++) {
4459
0
        gid_t *group_ids = session.gids->elts;
4460
4461
0
        if (group_ids[i] == gid) {
4462
0
          use_root_chown = FALSE;
4463
0
          break;
4464
0
        }
4465
0
      }
4466
0
    }
4467
4468
0
    if (use_root_chown == FALSE) {
4469
0
      use_mkdtemp = FALSE;
4470
0
    }
4471
0
  }
4472
4473
0
#ifdef HAVE_MKDTEMP
4474
0
  if (use_mkdtemp == TRUE) {
4475
0
    char *ptr;
4476
0
    struct stat st;
4477
4478
0
    ptr = strrchr(path, '/');
4479
0
    if (ptr == NULL) {
4480
0
      errno = EINVAL;
4481
0
      return -1;
4482
0
    }
4483
4484
0
    if (ptr != path) {
4485
0
      dst_dirlen = (ptr - path);
4486
0
      dst_dir = pstrndup(p, path, dst_dirlen);
4487
4488
0
    } else {
4489
0
      dst_dirlen = 1;
4490
0
      dst_dir = "/";
4491
0
    }
4492
4493
0
    res = lstat(dst_dir, &st);
4494
0
    if (res < 0) {
4495
0
      xerrno = errno;
4496
4497
0
      pr_log_pri(PR_LOG_WARNING,
4498
0
        "smkdir: unable to lstat(2) parent directory '%s': %s", dst_dir,
4499
0
        strerror(xerrno));
4500
0
      pr_trace_msg(trace_channel, 1,
4501
0
        "smkdir: unable to lstat(2) parent directory '%s': %s", dst_dir,
4502
0
        strerror(xerrno));
4503
4504
0
      errno = xerrno;
4505
0
      return -1;
4506
0
    }
4507
4508
0
    if (!S_ISDIR(st.st_mode) &&
4509
0
        !S_ISLNK(st.st_mode)) {
4510
0
      errno = EPERM;
4511
0
      return -1;
4512
0
    }
4513
4514
0
    if (st.st_mode & S_ISGID) {
4515
0
      set_sgid = TRUE;
4516
0
    }
4517
4518
    /* Allocate enough space for the temporary name: the length of the
4519
     * destination directory, a slash, 9 X's, 3 for the prefix, and 1 for the
4520
     * trailing NUL.
4521
     */
4522
0
    tmpl_len = dst_dirlen + 15;
4523
0
    tmpl = pcalloc(p, tmpl_len);
4524
0
    pr_snprintf(tmpl, tmpl_len-1, "%s/.dstXXXXXXXXX",
4525
0
      dst_dirlen > 1 ? dst_dir : "");
4526
4527
    /* Use mkdtemp(3) to create the temporary directory (in the same destination
4528
     * directory as the target path).
4529
     */
4530
0
    tmpl_path = mkdtemp(tmpl);
4531
0
    if (tmpl_path == NULL) {
4532
0
      xerrno = errno;
4533
4534
0
      pr_log_pri(PR_LOG_WARNING,
4535
0
        "smkdir: mkdtemp(3) failed to create directory using '%s': %s", tmpl,
4536
0
        strerror(xerrno));
4537
0
      pr_trace_msg(trace_channel, 1,
4538
0
        "smkdir: mkdtemp(3) failed to create directory using '%s': %s", tmpl,
4539
0
        strerror(xerrno));
4540
4541
0
      errno = xerrno;
4542
0
      return -1;
4543
0
    }
4544
4545
0
  } else {
4546
0
    res = pr_fsio_mkdir(path, mode);
4547
0
    if (res < 0) {
4548
0
      xerrno = errno;
4549
4550
0
      pr_trace_msg(trace_channel, 1,
4551
0
        "mkdir(2) failed to create directory '%s' with perms %04o: %s", path,
4552
0
        mode, strerror(xerrno));
4553
4554
0
      errno = xerrno;
4555
0
      return -1;
4556
0
    }
4557
4558
0
    tmpl_path = pstrdup(p, path);
4559
0
  }
4560
#else
4561
4562
  res = pr_fsio_mkdir(path, mode);
4563
  if (res < 0) {
4564
    xerrno = errno;
4565
4566
    pr_trace_msg(trace_channel, 1,
4567
      "mkdir(2) failed to create directory '%s' with perms %04o: %s", path,
4568
      mode, strerror(xerrno));
4569
4570
    errno = xerrno;
4571
    return -1;
4572
  }
4573
4574
  tmpl_path = pstrdup(p, path);
4575
#endif /* HAVE_MKDTEMP */
4576
4577
0
  if (use_mkdtemp == TRUE) {
4578
0
    mode_t mask, *dir_umask, perms;
4579
4580
    /* mkdtemp(3) creates a directory with 0700 perms; we are given the
4581
     * target mode (modulo the configured Umask).
4582
     */
4583
0
    dir_umask = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
4584
0
    if (dir_umask == NULL) {
4585
      /* If Umask was configured with a single parameter, then DirUmask
4586
       * would not be present; we still should check for Umask.
4587
       */
4588
0
      dir_umask = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
4589
0
    }
4590
4591
0
    if (dir_umask) {
4592
0
      mask = *dir_umask;
4593
4594
0
    } else {
4595
0
      mask = (mode_t) 0022;
4596
0
    }
4597
4598
0
    perms = (mode & ~mask);
4599
4600
0
    if (set_sgid) {
4601
0
      perms |= S_ISGID;
4602
0
    }
4603
4604
    /* If we're setting the SGID bit, we need to use root privs, in order
4605
     * to reliably set the SGID bit.  Sigh.
4606
     */
4607
0
    res = schmod_dir(p, tmpl_path, perms, set_sgid);
4608
0
    xerrno = errno;
4609
4610
0
    if (set_sgid) {
4611
0
      if (res < 0 &&
4612
0
          xerrno == EPERM) {
4613
        /* Try again, this time without root privs.  NFS situations which
4614
         * squash root privs could cause the above chmod(2) to fail; it
4615
         * might succeed now that we've dropped root privs (Bug#3962).
4616
         */
4617
0
        res = schmod_dir(p, tmpl_path, perms, FALSE);
4618
0
        xerrno = errno;
4619
0
      }
4620
0
    }
4621
4622
0
    if (res < 0) {
4623
0
      pr_log_pri(PR_LOG_WARNING, "chmod(%s) failed: %s", tmpl_path,
4624
0
        strerror(xerrno));
4625
4626
0
      (void) rmdir(tmpl_path);
4627
4628
0
      errno = xerrno;
4629
0
      return -1;
4630
0
    }
4631
0
  }
4632
4633
0
  if (uid != (uid_t) -1) {
4634
0
    if (use_root_chown) {
4635
0
      PRIVS_ROOT
4636
0
    }
4637
4638
0
    res = pr_fsio_lchown(tmpl_path, uid, gid);
4639
0
    xerrno = errno;
4640
4641
0
    if (use_root_chown) {
4642
0
      PRIVS_RELINQUISH
4643
0
    }
4644
4645
0
    if (res < 0) {
4646
0
      pr_log_pri(PR_LOG_WARNING, "lchown(%s) as root failed: %s", tmpl_path,
4647
0
        strerror(xerrno));
4648
4649
0
    } else {
4650
0
      if (gid != (gid_t) -1) {
4651
0
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s, GID %s successful",
4652
0
          tmpl_path, pr_uid2str(p, uid), pr_gid2str(p, gid));
4653
4654
0
      } else {
4655
0
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s successful",
4656
0
          tmpl_path, pr_uid2str(NULL, uid));
4657
0
      }
4658
0
    }
4659
4660
0
  } else if (gid != (gid_t) -1) {
4661
0
    if (use_root_chown) {
4662
0
      PRIVS_ROOT
4663
0
    }
4664
4665
0
    res = pr_fsio_lchown(tmpl_path, (uid_t) -1, gid);
4666
0
    xerrno = errno;
4667
4668
0
    if (use_root_chown) {
4669
0
      PRIVS_RELINQUISH
4670
0
    }
4671
4672
0
    if (res < 0) {
4673
0
      pr_log_pri(PR_LOG_WARNING, "%slchown(%s) failed: %s",
4674
0
        use_root_chown ? "root " : "", tmpl_path, strerror(xerrno));
4675
4676
0
    } else {
4677
0
      pr_log_debug(DEBUG2, "%slchown(%s) to GID %s successful",
4678
0
        use_root_chown ? "root " : "", tmpl_path, pr_gid2str(p, gid));
4679
0
    }
4680
0
  }
4681
4682
0
  if (use_mkdtemp == TRUE) {
4683
    /* Use rename(2) to move the temporary directory into place at the
4684
     * target path.
4685
     */
4686
0
    res = rename(tmpl_path, path);
4687
0
    if (res < 0) {
4688
0
      xerrno = errno;
4689
4690
0
      pr_log_pri(PR_LOG_INFO, "renaming '%s' to '%s' failed: %s", tmpl_path,
4691
0
        path, strerror(xerrno));
4692
4693
0
      (void) rmdir(tmpl_path);
4694
4695
0
#ifdef ENOTEMPTY
4696
0
      if (xerrno == ENOTEMPTY) {
4697
        /* If the rename(2) failed with "Directory not empty" (ENOTEMPTY),
4698
         * then change the errno to "File exists" (EEXIST), so that the
4699
         * error reported to the client is more indicative of the actual
4700
         * cause.
4701
         */
4702
0
        xerrno = EEXIST;
4703
0
      }
4704
0
#endif /* ENOTEMPTY */
4705
4706
0
      errno = xerrno;
4707
0
      return -1;
4708
0
    }
4709
0
  }
4710
4711
0
  pr_fs_clear_cache2(path);
4712
0
  return 0;
4713
0
}
4714
4715
0
int pr_fsio_rmdir(const char *path) {
4716
0
  int res;
4717
0
  pr_fs_t *fs;
4718
4719
0
  if (path == NULL) {
4720
0
    errno = EINVAL;
4721
0
    return -1;
4722
0
  }
4723
4724
0
  fs = lookup_dir_fs(path, FSIO_DIR_RMDIR);
4725
0
  if (fs == NULL) {
4726
0
    return -1;
4727
0
  }
4728
4729
  /* Find the first non-NULL custom rmdir handler.  If there are none,
4730
   * use the system rmdir.
4731
   */
4732
0
  while (fs && fs->fs_next && !fs->rmdir) {
4733
0
    fs = fs->fs_next;
4734
0
  }
4735
4736
0
  pr_trace_msg(trace_channel, 8, "using %s rmdir() for path '%s'", fs->fs_name,
4737
0
    path);
4738
0
  res = (fs->rmdir)(fs, path);
4739
0
  if (res == 0) {
4740
0
    pr_fs_clear_cache2(path);
4741
0
  }
4742
4743
0
  return res;
4744
0
}
4745
4746
0
int pr_fsio_rmdir_with_error(pool *p, const char *path, pr_error_t **err) {
4747
0
  int res;
4748
4749
0
  res = pr_fsio_rmdir(path);
4750
0
  if (res < 0) {
4751
0
    int xerrno = errno;
4752
4753
0
    if (p != NULL &&
4754
0
        err != NULL) {
4755
0
      *err = pr_error_create(p, xerrno);
4756
0
      if (pr_error_explain_rmdir(*err, path) < 0) {
4757
0
        pr_error_destroy(*err);
4758
0
        *err = NULL;
4759
0
      }
4760
0
    }
4761
4762
0
    errno = xerrno;
4763
0
  }
4764
4765
0
  return res;
4766
0
}
4767
4768
0
int pr_fsio_stat(const char *path, struct stat *st) {
4769
0
  pr_fs_t *fs = NULL;
4770
4771
0
  if (path == NULL ||
4772
0
      st == NULL) {
4773
0
    errno = EINVAL;
4774
0
    return -1;
4775
0
  }
4776
4777
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_STAT);
4778
0
  if (fs == NULL) {
4779
0
    return -1;
4780
0
  }
4781
4782
  /* Find the first non-NULL custom stat handler.  If there are none,
4783
   * use the system stat.
4784
   */
4785
0
  while (fs && fs->fs_next && !fs->stat) {
4786
0
    fs = fs->fs_next;
4787
0
  }
4788
4789
0
  pr_trace_msg(trace_channel, 8, "using %s stat() for path '%s'", fs->fs_name,
4790
0
    path);
4791
0
  return fs_cache_stat(fs ? fs : root_fs, path, st);
4792
0
}
4793
4794
int pr_fsio_stat_with_error(pool *p, const char *path, struct stat *st,
4795
0
    pr_error_t **err) {
4796
0
  int res;
4797
4798
0
  res = pr_fsio_stat(path, st);
4799
0
  if (res < 0) {
4800
0
    int xerrno = errno;
4801
4802
0
    if (p != NULL &&
4803
0
        err != NULL) {
4804
0
      *err = pr_error_create(p, xerrno);
4805
0
      if (pr_error_explain_stat(*err, path, st) < 0) {
4806
0
        pr_error_destroy(*err);
4807
0
        *err = NULL;
4808
0
      }
4809
0
    }
4810
4811
0
    errno = xerrno;
4812
0
  }
4813
4814
0
  return res;
4815
0
}
4816
4817
0
int pr_fsio_fstat(pr_fh_t *fh, struct stat *st) {
4818
0
  int res;
4819
0
  pr_fs_t *fs;
4820
4821
0
  if (fh == NULL ||
4822
0
      st == NULL) {
4823
0
    errno = EINVAL;
4824
0
    return -1;
4825
0
  }
4826
4827
  /* Find the first non-NULL custom fstat handler.  If there are none,
4828
   * use the system fstat.
4829
   */
4830
0
  fs = fh->fh_fs;
4831
0
  while (fs && fs->fs_next && !fs->fstat) {
4832
0
    fs = fs->fs_next;
4833
0
  }
4834
4835
0
  pr_trace_msg(trace_channel, 8, "using %s fstat() for path '%s'", fs->fs_name,
4836
0
    fh->fh_path);
4837
0
  res = (fs->fstat)(fh, fh->fh_fd, st);
4838
4839
0
  return res;
4840
0
}
4841
4842
0
int pr_fsio_lstat(const char *path, struct stat *st) {
4843
0
  pr_fs_t *fs;
4844
4845
0
  if (path == NULL ||
4846
0
      st == NULL) {
4847
0
    errno = EINVAL;
4848
0
    return -1;
4849
0
  }
4850
4851
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LSTAT);
4852
0
  if (fs == NULL) {
4853
0
    return -1;
4854
0
  }
4855
4856
  /* Find the first non-NULL custom lstat handler.  If there are none,
4857
   * use the system lstat.
4858
   */
4859
0
  while (fs && fs->fs_next && !fs->lstat) {
4860
0
    fs = fs->fs_next;
4861
0
  }
4862
4863
0
  pr_trace_msg(trace_channel, 8, "using %s lstat() for path '%s'", fs->fs_name,
4864
0
    path);
4865
0
  return fs_cache_lstat(fs ? fs : root_fs, path, st);
4866
0
}
4867
4868
int pr_fsio_lstat_with_error(pool *p, const char *path, struct stat *st,
4869
0
    pr_error_t **err) {
4870
0
  int res;
4871
4872
0
  res = pr_fsio_lstat(path, st);
4873
0
  if (res < 0) {
4874
0
    int xerrno = errno;
4875
4876
0
    if (p != NULL &&
4877
0
        err != NULL) {
4878
0
      *err = pr_error_create(p, xerrno);
4879
0
      if (pr_error_explain_lstat(*err, path, st) < 0) {
4880
0
        pr_error_destroy(*err);
4881
0
        *err = NULL;
4882
0
      }
4883
0
    }
4884
4885
0
    errno = xerrno;
4886
0
  }
4887
4888
0
  return res;
4889
0
}
4890
4891
0
int pr_fsio_readlink(const char *path, char *buf, size_t buflen) {
4892
0
  int res;
4893
0
  pr_fs_t *fs;
4894
4895
0
  if (path == NULL ||
4896
0
      buf == NULL) {
4897
0
    errno = EINVAL;
4898
0
    return -1;
4899
0
  }
4900
4901
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_READLINK);
4902
0
  if (fs == NULL) {
4903
0
    return -1;
4904
0
  }
4905
4906
  /* Find the first non-NULL custom readlink handler.  If there are none,
4907
   * use the system readlink.
4908
   */
4909
0
  while (fs && fs->fs_next && !fs->readlink) {
4910
0
    fs = fs->fs_next;
4911
0
  }
4912
4913
0
  pr_trace_msg(trace_channel, 8, "using %s readlink() for path '%s'",
4914
0
    fs->fs_name, path);
4915
0
  res = (fs->readlink)(fs, path, buf, buflen);
4916
4917
0
  return res;
4918
0
}
4919
4920
/* pr_fs_glob() is just a wrapper for glob(3), setting the various gl_
4921
 * callbacks to our fs functions.
4922
 */
4923
int pr_fs_glob(const char *pattern, int flags,
4924
0
    int (*errfunc)(const char *, int), glob_t *pglob) {
4925
4926
0
  if (pattern == NULL ||
4927
0
      pglob == NULL) {
4928
0
    errno = EINVAL;
4929
0
    return -1;
4930
0
  }
4931
4932
0
  flags |= GLOB_ALTDIRFUNC;
4933
4934
0
  pglob->gl_closedir = (void (*)(void *)) pr_fsio_closedir;
4935
0
  pglob->gl_readdir = pr_fsio_readdir;
4936
0
  pglob->gl_opendir = pr_fsio_opendir;
4937
0
  pglob->gl_lstat = pr_fsio_lstat;
4938
0
  pglob->gl_stat = pr_fsio_stat;
4939
4940
0
  return glob(pattern, flags, errfunc, pglob);
4941
0
}
4942
4943
0
void pr_fs_globfree(glob_t *pglob) {
4944
0
  if (pglob != NULL) {
4945
0
    globfree(pglob);
4946
0
  }
4947
0
}
4948
4949
0
int pr_fsio_rename(const char *rnfr, const char *rnto) {
4950
0
  int res;
4951
0
  pr_fs_t *from_fs, *to_fs, *fs;
4952
4953
0
  if (rnfr == NULL ||
4954
0
      rnto == NULL) {
4955
0
    errno = EINVAL;
4956
0
    return -1;
4957
0
  }
4958
4959
0
  from_fs = lookup_file_fs(rnfr, NULL, FSIO_FILE_RENAME);
4960
0
  if (from_fs == NULL) {
4961
0
    return -1;
4962
0
  }
4963
4964
0
  to_fs = lookup_file_fs(rnto, NULL, FSIO_FILE_RENAME);
4965
0
  if (to_fs == NULL) {
4966
0
    return -1;
4967
0
  }
4968
4969
0
  if (from_fs->allow_xdev_rename == FALSE ||
4970
0
      to_fs->allow_xdev_rename == FALSE) {
4971
0
    if (from_fs != to_fs) {
4972
0
      errno = EXDEV;
4973
0
      return -1;
4974
0
    }
4975
0
  }
4976
4977
0
  fs = to_fs;
4978
4979
  /* Find the first non-NULL custom rename handler.  If there are none,
4980
   * use the system rename.
4981
   */
4982
0
  while (fs && fs->fs_next && !fs->rename) {
4983
0
    fs = fs->fs_next;
4984
0
  }
4985
4986
0
  pr_trace_msg(trace_channel, 8, "using %s rename() for paths '%s', '%s'",
4987
0
    fs->fs_name, rnfr, rnto);
4988
0
  res = (fs->rename)(fs, rnfr, rnto);
4989
0
  if (res == 0) {
4990
0
    pr_fs_clear_cache2(rnfr);
4991
0
    pr_fs_clear_cache2(rnto);
4992
0
  }
4993
4994
0
  return res;
4995
0
}
4996
4997
int pr_fsio_rename_with_error(pool *p, const char *rnfr, const char *rnto,
4998
0
    pr_error_t **err) {
4999
0
  int res;
5000
5001
0
  res = pr_fsio_rename(rnfr, rnto);
5002
0
  if (res < 0) {
5003
0
    int xerrno = errno;
5004
5005
0
    if (p != NULL &&
5006
0
        err != NULL) {
5007
0
      *err = pr_error_create(p, xerrno);
5008
0
      if (pr_error_explain_rename(*err, rnfr, rnto) < 0) {
5009
0
        pr_error_destroy(*err);
5010
0
        *err = NULL;
5011
0
      }
5012
0
    }
5013
5014
0
    errno = xerrno;
5015
0
  }
5016
5017
0
  return res;
5018
0
}
5019
5020
0
int pr_fsio_unlink(const char *name) {
5021
0
  int res;
5022
0
  pr_fs_t *fs;
5023
5024
0
  if (name == NULL) {
5025
0
    errno = EINVAL;
5026
0
    return -1;
5027
0
  }
5028
5029
0
  fs = lookup_file_fs(name, NULL, FSIO_FILE_UNLINK);
5030
0
  if (fs == NULL) {
5031
0
    return -1;
5032
0
  }
5033
5034
  /* Find the first non-NULL custom unlink handler.  If there are none,
5035
   * use the system unlink.
5036
   */
5037
0
  while (fs && fs->fs_next && !fs->unlink) {
5038
0
    fs = fs->fs_next;
5039
0
  }
5040
5041
0
  pr_trace_msg(trace_channel, 8, "using %s unlink() for path '%s'",
5042
0
    fs->fs_name, name);
5043
0
  res = (fs->unlink)(fs, name);
5044
0
  if (res == 0) {
5045
0
    pr_fs_clear_cache2(name);
5046
0
  }
5047
5048
0
  return res;
5049
0
}
5050
5051
0
int pr_fsio_unlink_with_error(pool *p, const char *path, pr_error_t **err) {
5052
0
  int res;
5053
5054
0
  res = pr_fsio_unlink(path);
5055
0
  if (res < 0) {
5056
0
    int xerrno = errno;
5057
5058
0
    if (p != NULL &&
5059
0
        err != NULL) {
5060
0
      *err = pr_error_create(p, xerrno);
5061
0
      if (pr_error_explain_unlink(*err, path) < 0) {
5062
0
        pr_error_destroy(*err);
5063
0
        *err = NULL;
5064
0
      }
5065
0
    }
5066
5067
0
    errno = xerrno;
5068
0
  }
5069
5070
0
  return res;
5071
0
}
5072
5073
0
pr_fh_t *pr_fsio_open_canon(const char *name, int flags) {
5074
0
  char *deref = NULL;
5075
0
  pool *tmp_pool = NULL;
5076
0
  pr_fh_t *fh = NULL;
5077
0
  pr_fs_t *fs = NULL;
5078
5079
0
  if (name == NULL) {
5080
0
    errno = EINVAL;
5081
0
    return NULL;
5082
0
  }
5083
5084
0
  fs = lookup_file_canon_fs(name, &deref, FSIO_FILE_OPEN);
5085
0
  if (fs == NULL) {
5086
0
    return NULL;
5087
0
  }
5088
5089
  /* Allocate a filehandle. */
5090
0
  tmp_pool = make_sub_pool(fs->fs_pool);
5091
0
  pr_pool_tag(tmp_pool, "pr_fsio_open_canon() subpool");
5092
5093
0
  fh = pcalloc(tmp_pool, sizeof(pr_fh_t));
5094
0
  fh->fh_pool = tmp_pool;
5095
0
  fh->fh_path = pstrdup(fh->fh_pool, name);
5096
0
  fh->fh_fd = -1;
5097
0
  fh->fh_buf = NULL;
5098
0
  fh->fh_fs = fs;
5099
5100
  /* Find the first non-NULL custom open handler.  If there are none,
5101
   * use the system open.
5102
   */
5103
0
  while (fs && fs->fs_next && !fs->open) {
5104
0
    fs = fs->fs_next;
5105
0
  }
5106
5107
0
  pr_trace_msg(trace_channel, 8, "using %s open() for path '%s'", fs->fs_name,
5108
0
    name);
5109
0
  fh->fh_fd = (fs->open)(fh, deref, flags);
5110
0
  if (fh->fh_fd < 0) {
5111
0
    int xerrno = errno;
5112
5113
0
    destroy_pool(fh->fh_pool);
5114
5115
0
    errno = xerrno;
5116
0
    return NULL;
5117
0
  }
5118
5119
0
  if ((flags & O_CREAT) ||
5120
0
      (flags & O_TRUNC)) {
5121
0
    pr_fs_clear_cache2(name);
5122
0
  }
5123
5124
0
  if (fcntl(fh->fh_fd, F_SETFD, FD_CLOEXEC) < 0) {
5125
0
    if (errno != EBADF) {
5126
0
      pr_trace_msg(trace_channel, 1, "error setting CLOEXEC on file fd %d: %s",
5127
0
        fh->fh_fd, strerror(errno));
5128
0
    }
5129
0
  }
5130
5131
0
  return fh;
5132
0
}
5133
5134
0
pr_fh_t *pr_fsio_open(const char *name, int flags) {
5135
0
  pool *tmp_pool = NULL;
5136
0
  pr_fh_t *fh = NULL;
5137
0
  pr_fs_t *fs = NULL;
5138
5139
0
  if (name == NULL) {
5140
0
    errno = EINVAL;
5141
0
    return NULL;
5142
0
  }
5143
5144
0
  fs = lookup_file_fs(name, NULL, FSIO_FILE_OPEN);
5145
0
  if (fs == NULL) {
5146
0
    return NULL;
5147
0
  }
5148
5149
  /* Allocate a filehandle. */
5150
0
  tmp_pool = make_sub_pool(fs->fs_pool);
5151
0
  pr_pool_tag(tmp_pool, "pr_fsio_open() subpool");
5152
5153
0
  fh = pcalloc(tmp_pool, sizeof(pr_fh_t));
5154
0
  fh->fh_pool = tmp_pool;
5155
0
  fh->fh_path = pstrdup(fh->fh_pool, name);
5156
0
  fh->fh_fd = -1;
5157
0
  fh->fh_buf = NULL;
5158
0
  fh->fh_fs = fs;
5159
5160
  /* Find the first non-NULL custom open handler.  If there are none,
5161
   * use the system open.
5162
   */
5163
0
  while (fs && fs->fs_next && !fs->open) {
5164
0
    fs = fs->fs_next;
5165
0
  }
5166
5167
0
  pr_trace_msg(trace_channel, 8, "using %s open() for path '%s'", fs->fs_name,
5168
0
    name);
5169
0
  fh->fh_fd = (fs->open)(fh, name, flags);
5170
0
  if (fh->fh_fd < 0) {
5171
0
    int xerrno = errno;
5172
5173
0
    destroy_pool(fh->fh_pool);
5174
5175
0
    errno = xerrno;
5176
0
    return NULL;
5177
0
  }
5178
5179
0
  if ((flags & O_CREAT) ||
5180
0
      (flags & O_TRUNC)) {
5181
0
    pr_fs_clear_cache2(name);
5182
0
  }
5183
5184
0
  if (fcntl(fh->fh_fd, F_SETFD, FD_CLOEXEC) < 0) {
5185
0
    if (errno != EBADF) {
5186
0
      pr_trace_msg(trace_channel, 1, "error setting CLOEXEC on file fd %d: %s",
5187
0
        fh->fh_fd, strerror(errno));
5188
0
    }
5189
0
  }
5190
5191
0
  return fh;
5192
0
}
5193
5194
pr_fh_t *pr_fsio_open_with_error(pool *p, const char *name, int flags,
5195
0
    pr_error_t **err) {
5196
0
  pr_fh_t *fh;
5197
5198
0
  fh = pr_fsio_open(name, flags);
5199
0
  if (fh == NULL) {
5200
0
    int xerrno = errno;
5201
5202
0
    if (p != NULL &&
5203
0
        err != NULL) {
5204
0
      *err = pr_error_create(p, xerrno);
5205
0
      if (pr_error_explain_open(*err, name, flags, PR_OPEN_MODE) < 0) {
5206
0
        pr_error_destroy(*err);
5207
0
        *err = NULL;
5208
0
      }
5209
0
    }
5210
5211
0
    errno = xerrno;
5212
0
  }
5213
5214
0
  return fh;
5215
0
}
5216
5217
0
int pr_fsio_close(pr_fh_t *fh) {
5218
0
  int res = 0, xerrno = 0;
5219
0
  pr_fs_t *fs;
5220
5221
0
  if (fh == NULL) {
5222
0
    errno = EINVAL;
5223
0
    return -1;
5224
0
  }
5225
5226
  /* Find the first non-NULL custom close handler.  If there are none,
5227
   * use the system close.
5228
   */
5229
0
  fs = fh->fh_fs;
5230
0
  while (fs && fs->fs_next && !fs->close) {
5231
0
    fs = fs->fs_next;
5232
0
  }
5233
5234
0
  pr_trace_msg(trace_channel, 8, "using %s close() for path '%s'", fs->fs_name,
5235
0
    fh->fh_path);
5236
0
  res = (fs->close)(fh, fh->fh_fd);
5237
0
  xerrno = errno;
5238
5239
0
  if (res == 0) {
5240
0
    pr_fs_clear_cache2(fh->fh_path);
5241
0
  }
5242
5243
  /* Make sure to scrub any buffered memory, too. */
5244
0
  if (fh->fh_buf != NULL) {
5245
0
    pr_buffer_t *pbuf;
5246
5247
0
    pbuf = fh->fh_buf;
5248
0
    pr_memscrub(pbuf->buf, pbuf->buflen);
5249
0
  }
5250
5251
0
  if (fh->fh_pool != NULL) {
5252
0
    destroy_pool(fh->fh_pool);
5253
0
  }
5254
5255
0
  errno = xerrno;
5256
0
  return res;
5257
0
}
5258
5259
0
int pr_fsio_close_with_error(pool *p, pr_fh_t *fh, pr_error_t **err) {
5260
0
  int res;
5261
5262
0
  res = pr_fsio_close(fh);
5263
0
  if (res < 0) {
5264
0
    int xerrno = errno;
5265
5266
0
    if (p != NULL &&
5267
0
        err != NULL) {
5268
0
      int fd = -1;
5269
5270
0
      *err = pr_error_create(p, xerrno);
5271
5272
0
      if (fh != NULL) {
5273
0
        fd = fh->fh_fd;
5274
0
      }
5275
5276
0
      if (pr_error_explain_close(*err, fd) < 0) {
5277
0
        pr_error_destroy(*err);
5278
0
        *err = NULL;
5279
0
      }
5280
0
    }
5281
5282
0
    errno = xerrno;
5283
0
  }
5284
5285
0
  return res;
5286
0
}
5287
5288
0
ssize_t pr_fsio_pread(pr_fh_t *fh, void *buf, size_t size, off_t offset) {
5289
0
  ssize_t res;
5290
0
  pr_fs_t *fs;
5291
5292
0
  if (fh == NULL ||
5293
0
      buf == NULL ||
5294
0
      size == 0) {
5295
0
    errno = EINVAL;
5296
0
    return -1;
5297
0
  }
5298
5299
  /* Find the first non-NULL custom pread handler.  If there are none,
5300
   * use the system pread.
5301
   */
5302
0
  fs = fh->fh_fs;
5303
0
  while (fs && fs->fs_next && !fs->pread) {
5304
0
    fs = fs->fs_next;
5305
0
  }
5306
5307
0
  pr_trace_msg(trace_channel, 8, "using %s pread() for path '%s' (%lu bytes, %"
5308
0
    PR_LU " offset)", fs->fs_name, fh->fh_path, (unsigned long) size,
5309
0
    (pr_off_t) offset);
5310
0
  res = (fs->pread)(fh, fh->fh_fd, buf, size, offset);
5311
5312
0
  return res;
5313
0
}
5314
5315
0
int pr_fsio_read(pr_fh_t *fh, char *buf, size_t size) {
5316
0
  int res;
5317
0
  pr_fs_t *fs;
5318
5319
0
  if (fh == NULL ||
5320
0
      buf == NULL ||
5321
0
      size == 0) {
5322
0
    errno = EINVAL;
5323
0
    return -1;
5324
0
  }
5325
5326
  /* Find the first non-NULL custom read handler.  If there are none,
5327
   * use the system read.
5328
   */
5329
0
  fs = fh->fh_fs;
5330
0
  while (fs && fs->fs_next && !fs->read) {
5331
0
    fs = fs->fs_next;
5332
0
  }
5333
5334
0
  pr_trace_msg(trace_channel, 8, "using %s read() for path '%s' (%lu bytes)",
5335
0
    fs->fs_name, fh->fh_path, (unsigned long) size);
5336
0
  res = (fs->read)(fh, fh->fh_fd, buf, size);
5337
5338
0
  return res;
5339
0
}
5340
5341
int pr_fsio_read_with_error(pool *p, pr_fh_t *fh, char *buf, size_t sz,
5342
0
    pr_error_t **err) {
5343
0
  int res;
5344
5345
0
  res = pr_fsio_read(fh, buf, sz);
5346
0
  if (res < 0) {
5347
0
    int xerrno = errno;
5348
5349
0
    if (p != NULL &&
5350
0
        err != NULL) {
5351
0
      int fd = -1;
5352
5353
0
      if (fh != NULL) {
5354
0
        fd = fh->fh_fd;
5355
0
      }
5356
5357
0
      *err = pr_error_create(p, xerrno);
5358
0
      if (pr_error_explain_read(*err, fd, buf, sz) < 0) {
5359
0
        pr_error_destroy(*err);
5360
0
        *err = NULL;
5361
0
      }
5362
0
    }
5363
5364
0
    errno = xerrno;
5365
0
  }
5366
5367
0
  return res;
5368
0
}
5369
5370
ssize_t pr_fsio_pwrite(pr_fh_t *fh, const void *buf, size_t size,
5371
0
    off_t offset) {
5372
0
  ssize_t res;
5373
0
  pr_fs_t *fs;
5374
5375
0
  if (fh == NULL ||
5376
0
      buf == NULL) {
5377
0
    errno = EINVAL;
5378
0
    return -1;
5379
0
  }
5380
5381
  /* Find the first non-NULL custom pwrite handler.  If there are none,
5382
   * use the system pwrite.
5383
   */
5384
0
  fs = fh->fh_fs;
5385
0
  while (fs && fs->fs_next && !fs->pwrite) {
5386
0
    fs = fs->fs_next;
5387
0
  }
5388
5389
0
  pr_trace_msg(trace_channel, 8, "using %s pwrite() for path '%s' (%lu bytes, %"
5390
0
    PR_LU " offset)", fs->fs_name, fh->fh_path, (unsigned long) size,
5391
0
    (pr_off_t) offset);
5392
0
  res = (fs->pwrite)(fh, fh->fh_fd, buf, size, offset);
5393
5394
0
  return res;
5395
0
}
5396
5397
0
int pr_fsio_write(pr_fh_t *fh, const char *buf, size_t size) {
5398
0
  int res;
5399
0
  pr_fs_t *fs;
5400
5401
0
  if (fh == NULL ||
5402
0
      buf == NULL) {
5403
0
    errno = EINVAL;
5404
0
    return -1;
5405
0
  }
5406
5407
  /* Find the first non-NULL custom write handler.  If there are none,
5408
   * use the system write.
5409
   */
5410
0
  fs = fh->fh_fs;
5411
0
  while (fs && fs->fs_next && !fs->write) {
5412
0
    fs = fs->fs_next;
5413
0
  }
5414
5415
0
  pr_trace_msg(trace_channel, 8, "using %s write() for path '%s' (%lu bytes)",
5416
0
    fs->fs_name, fh->fh_path, (unsigned long) size);
5417
0
  res = (fs->write)(fh, fh->fh_fd, buf, size);
5418
5419
0
  return res;
5420
0
}
5421
5422
int pr_fsio_write_with_error(pool *p, pr_fh_t *fh, const char *buf, size_t sz,
5423
0
    pr_error_t **err) {
5424
0
  int res;
5425
5426
0
  res = pr_fsio_write(fh, buf, sz);
5427
0
  if (res < 0) {
5428
0
    int xerrno = errno;
5429
5430
0
    if (p != NULL &&
5431
0
        err != NULL) {
5432
0
      int fd = -1;
5433
5434
0
      if (fh != NULL) {
5435
0
        fd = fh->fh_fd;
5436
0
      }
5437
5438
0
      *err = pr_error_create(p, xerrno);
5439
0
      if (pr_error_explain_write(*err, fd, buf, sz) < 0) {
5440
0
        pr_error_destroy(*err);
5441
0
        *err = NULL;
5442
0
      }
5443
0
    }
5444
5445
0
    errno = xerrno;
5446
0
  }
5447
5448
0
  return res;
5449
0
}
5450
5451
0
off_t pr_fsio_lseek(pr_fh_t *fh, off_t offset, int whence) {
5452
0
  off_t res;
5453
0
  pr_fs_t *fs;
5454
5455
0
  if (fh == NULL) {
5456
0
    errno = EINVAL;
5457
0
    return -1;
5458
0
  }
5459
5460
  /* Find the first non-NULL custom lseek handler.  If there are none,
5461
   * use the system lseek.
5462
   */
5463
0
  fs = fh->fh_fs;
5464
0
  while (fs && fs->fs_next && !fs->lseek) {
5465
0
    fs = fs->fs_next;
5466
0
  }
5467
5468
0
  pr_trace_msg(trace_channel, 8, "using %s lseek() for path '%s'", fs->fs_name,
5469
0
    fh->fh_path);
5470
0
  res = (fs->lseek)(fh, fh->fh_fd, offset, whence);
5471
5472
0
  return res;
5473
0
}
5474
5475
0
int pr_fsio_link(const char *target_path, const char *link_path) {
5476
0
  int res;
5477
0
  pr_fs_t *target_fs, *link_fs, *fs;
5478
5479
0
  if (target_path == NULL ||
5480
0
      link_path == NULL) {
5481
0
    errno = EINVAL;
5482
0
    return -1;
5483
0
  }
5484
5485
0
  target_fs = lookup_file_fs(target_path, NULL, FSIO_FILE_LINK);
5486
0
  if (target_fs == NULL) {
5487
0
    return -1;
5488
0
  }
5489
5490
0
  link_fs = lookup_file_fs(link_path, NULL, FSIO_FILE_LINK);
5491
0
  if (link_fs == NULL) {
5492
0
    return -1;
5493
0
  }
5494
5495
0
  if (target_fs->allow_xdev_link == FALSE ||
5496
0
      link_fs->allow_xdev_link == FALSE) {
5497
0
    if (target_fs != link_fs) {
5498
0
      errno = EXDEV;
5499
0
      return -1;
5500
0
    }
5501
0
  }
5502
5503
0
  fs = link_fs;
5504
5505
  /* Find the first non-NULL custom link handler.  If there are none,
5506
   * use the system link.
5507
   */
5508
0
  while (fs && fs->fs_next && !fs->link) {
5509
0
    fs = fs->fs_next;
5510
0
  }
5511
5512
0
  pr_trace_msg(trace_channel, 8, "using %s link() for paths '%s', '%s'",
5513
0
    fs->fs_name, target_path, link_path);
5514
0
  res = (fs->link)(fs, target_path, link_path);
5515
0
  if (res == 0) {
5516
0
    pr_fs_clear_cache2(link_path);
5517
0
  }
5518
5519
0
  return res;
5520
0
}
5521
5522
0
int pr_fsio_symlink(const char *target_path, const char *link_path) {
5523
0
  int res;
5524
0
  pr_fs_t *fs;
5525
5526
0
  if (target_path == NULL ||
5527
0
      link_path == NULL) {
5528
0
    errno = EINVAL;
5529
0
    return -1;
5530
0
  }
5531
5532
0
  fs = lookup_file_fs(link_path, NULL, FSIO_FILE_SYMLINK);
5533
0
  if (fs == NULL) {
5534
0
    return -1;
5535
0
  }
5536
5537
  /* Find the first non-NULL custom symlink handler.  If there are none,
5538
   * use the system symlink.
5539
   */
5540
0
  while (fs && fs->fs_next && !fs->symlink) {
5541
0
    fs = fs->fs_next;
5542
0
  }
5543
5544
0
  pr_trace_msg(trace_channel, 8, "using %s symlink() for path '%s'",
5545
0
    fs->fs_name, link_path);
5546
0
  res = (fs->symlink)(fs, target_path, link_path);
5547
0
  if (res == 0) {
5548
0
    pr_fs_clear_cache2(link_path);
5549
0
  }
5550
5551
0
  return res;
5552
0
}
5553
5554
0
int pr_fsio_ftruncate(pr_fh_t *fh, off_t len) {
5555
0
  int res;
5556
0
  pr_fs_t *fs;
5557
5558
0
  if (fh == NULL) {
5559
0
    errno = EINVAL;
5560
0
    return -1;
5561
0
  }
5562
5563
  /* Find the first non-NULL custom ftruncate handler.  If there are none,
5564
   * use the system ftruncate.
5565
   */
5566
0
  fs = fh->fh_fs;
5567
0
  while (fs && fs->fs_next && !fs->ftruncate) {
5568
0
    fs = fs->fs_next;
5569
0
  }
5570
5571
0
  pr_trace_msg(trace_channel, 8, "using %s ftruncate() for path '%s'",
5572
0
    fs->fs_name, fh->fh_path);
5573
0
  res = (fs->ftruncate)(fh, fh->fh_fd, len);
5574
0
  if (res == 0) {
5575
0
    pr_fs_clear_cache2(fh->fh_path);
5576
5577
    /* Clear any read buffer. */
5578
0
    if (fh->fh_buf != NULL) {
5579
0
      fh->fh_buf->current = fh->fh_buf->buf;
5580
0
      fh->fh_buf->remaining = fh->fh_buf->buflen;
5581
0
    }
5582
0
  }
5583
5584
0
  return res;
5585
0
}
5586
5587
0
int pr_fsio_truncate(const char *path, off_t len) {
5588
0
  int res;
5589
0
  pr_fs_t *fs;
5590
5591
0
  if (path == NULL) {
5592
0
    errno = EINVAL;
5593
0
    return -1;
5594
0
  }
5595
5596
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_TRUNC);
5597
0
  if (fs == NULL) {
5598
0
    return -1;
5599
0
  }
5600
5601
  /* Find the first non-NULL custom truncate handler.  If there are none,
5602
   * use the system truncate.
5603
   */
5604
0
  while (fs && fs->fs_next && !fs->truncate) {
5605
0
    fs = fs->fs_next;
5606
0
  }
5607
5608
0
  pr_trace_msg(trace_channel, 8, "using %s truncate() for path '%s'",
5609
0
    fs->fs_name, path);
5610
0
  res = (fs->truncate)(fs, path, len);
5611
0
  if (res == 0) {
5612
0
    pr_fs_clear_cache2(path);
5613
0
  }
5614
5615
0
  return res;
5616
0
}
5617
5618
0
int pr_fsio_chmod(const char *name, mode_t mode) {
5619
0
  int res;
5620
0
  pr_fs_t *fs;
5621
5622
0
  if (name == NULL) {
5623
0
    errno = EINVAL;
5624
0
    return -1;
5625
0
  }
5626
5627
0
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHMOD);
5628
0
  if (fs == NULL) {
5629
0
    return -1;
5630
0
  }
5631
5632
  /* Find the first non-NULL custom chmod handler.  If there are none,
5633
   * use the system chmod.
5634
   */
5635
0
  while (fs && fs->fs_next && !fs->chmod) {
5636
0
    fs = fs->fs_next;
5637
0
  }
5638
5639
0
  pr_trace_msg(trace_channel, 8, "using %s chmod() for path '%s'",
5640
0
    fs->fs_name, name);
5641
0
  res = (fs->chmod)(fs, name, mode);
5642
0
  if (res == 0) {
5643
0
    pr_fs_clear_cache2(name);
5644
0
  }
5645
5646
0
  return res;
5647
0
}
5648
5649
int pr_fsio_chmod_with_error(pool *p, const char *path, mode_t mode,
5650
0
    pr_error_t **err) {
5651
0
  int res;
5652
5653
0
  res = pr_fsio_chmod(path, mode);
5654
0
  if (res < 0) {
5655
0
    int xerrno = errno;
5656
5657
0
    if (p != NULL &&
5658
0
        err != NULL) {
5659
0
      *err = pr_error_create(p, xerrno);
5660
0
      if (pr_error_explain_chmod(*err, path, mode) < 0) {
5661
0
        pr_error_destroy(*err);
5662
0
        *err = NULL;
5663
0
      }
5664
0
    }
5665
5666
0
    errno = xerrno;
5667
0
  }
5668
5669
0
  return res;
5670
0
}
5671
5672
0
int pr_fsio_fchmod(pr_fh_t *fh, mode_t mode) {
5673
0
  int res;
5674
0
  pr_fs_t *fs;
5675
5676
0
  if (fh == NULL) {
5677
0
    errno = EINVAL;
5678
0
    return -1;
5679
0
  }
5680
5681
  /* Find the first non-NULL custom fchmod handler.  If there are none, use
5682
   * the system fchmod.
5683
   */
5684
0
  fs = fh->fh_fs;
5685
0
  while (fs && fs->fs_next && !fs->fchmod) {
5686
0
    fs = fs->fs_next;
5687
0
  }
5688
5689
0
  pr_trace_msg(trace_channel, 8, "using %s fchmod() for path '%s'",
5690
0
    fs->fs_name, fh->fh_path);
5691
0
  res = (fs->fchmod)(fh, fh->fh_fd, mode);
5692
0
  if (res == 0) {
5693
0
    pr_fs_clear_cache2(fh->fh_path);
5694
0
  }
5695
5696
0
  return res;
5697
0
}
5698
5699
int pr_fsio_fchmod_with_error(pool *p, pr_fh_t *fh, mode_t mode,
5700
0
    pr_error_t **err) {
5701
0
  int res;
5702
5703
0
  res = pr_fsio_fchmod(fh, mode);
5704
0
  if (res < 0) {
5705
0
    int xerrno = errno;
5706
5707
0
    if (p != NULL &&
5708
0
        err != NULL) {
5709
0
      int fd = -1;
5710
5711
0
      if (fh != NULL) {
5712
0
        fd = fh->fh_fd;
5713
0
      }
5714
5715
0
      *err = pr_error_create(p, xerrno);
5716
0
      if (pr_error_explain_fchmod(*err, fd, mode) < 0) {
5717
0
        pr_error_destroy(*err);
5718
0
        *err = NULL;
5719
0
      }
5720
0
    }
5721
5722
0
    errno = xerrno;
5723
0
  }
5724
5725
0
  return res;
5726
0
}
5727
5728
0
int pr_fsio_chown(const char *name, uid_t uid, gid_t gid) {
5729
0
  int res;
5730
0
  pr_fs_t *fs;
5731
5732
0
  if (name == NULL) {
5733
0
    errno = EINVAL;
5734
0
    return -1;
5735
0
  }
5736
5737
0
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHOWN);
5738
0
  if (fs == NULL) {
5739
0
    return -1;
5740
0
  }
5741
5742
  /* Find the first non-NULL custom chown handler.  If there are none,
5743
   * use the system chown.
5744
   */
5745
0
  while (fs && fs->fs_next && !fs->chown) {
5746
0
    fs = fs->fs_next;
5747
0
  }
5748
5749
0
  pr_trace_msg(trace_channel, 8, "using %s chown() for path '%s'",
5750
0
    fs->fs_name, name);
5751
0
  res = (fs->chown)(fs, name, uid, gid);
5752
0
  if (res == 0) {
5753
0
    pr_fs_clear_cache2(name);
5754
0
  }
5755
5756
0
  return res;
5757
0
}
5758
5759
int pr_fsio_chown_with_error(pool *p, const char *path, uid_t uid, gid_t gid,
5760
0
    pr_error_t **err) {
5761
0
  int res;
5762
5763
0
  res = pr_fsio_chown(path, uid, gid);
5764
0
  if (res < 0) {
5765
0
    int xerrno = errno;
5766
5767
0
    if (p != NULL &&
5768
0
        err != NULL) {
5769
0
      *err = pr_error_create(p, xerrno);
5770
0
      if (pr_error_explain_chown(*err, path, uid, gid) < 0) {
5771
0
        pr_error_destroy(*err);
5772
0
        *err = NULL;
5773
0
      }
5774
0
    }
5775
5776
0
    errno = xerrno;
5777
0
  }
5778
5779
0
  return res;
5780
0
}
5781
5782
0
int pr_fsio_fchown(pr_fh_t *fh, uid_t uid, gid_t gid) {
5783
0
  int res;
5784
0
  pr_fs_t *fs;
5785
5786
0
  if (fh == NULL) {
5787
0
    errno = EINVAL;
5788
0
    return -1;
5789
0
  }
5790
5791
  /* Find the first non-NULL custom fchown handler.  If there are none, use
5792
   * the system fchown.
5793
   */
5794
0
  fs = fh->fh_fs;
5795
0
  while (fs && fs->fs_next && !fs->fchown) {
5796
0
    fs = fs->fs_next;
5797
0
  }
5798
5799
0
  pr_trace_msg(trace_channel, 8, "using %s fchown() for path '%s'",
5800
0
    fs->fs_name, fh->fh_path);
5801
0
  res = (fs->fchown)(fh, fh->fh_fd, uid, gid);
5802
0
  if (res == 0) {
5803
0
    pr_fs_clear_cache2(fh->fh_path);
5804
0
  }
5805
5806
0
  return res;
5807
0
}
5808
5809
int pr_fsio_fchown_with_error(pool *p, pr_fh_t *fh, uid_t uid, gid_t gid,
5810
0
    pr_error_t **err) {
5811
0
  int res;
5812
5813
0
  res = pr_fsio_fchown(fh, uid, gid);
5814
0
  if (res < 0) {
5815
0
    int xerrno = errno;
5816
5817
0
    if (p != NULL &&
5818
0
        err != NULL) {
5819
0
      int fd = -1;
5820
5821
0
      if (fh != NULL) {
5822
0
        fd = fh->fh_fd;
5823
0
      }
5824
5825
0
      *err = pr_error_create(p, xerrno);
5826
0
      if (pr_error_explain_fchown(*err, fd, uid, gid) < 0) {
5827
0
        pr_error_destroy(*err);
5828
0
        *err = NULL;
5829
0
      }
5830
0
    }
5831
5832
0
    errno = xerrno;
5833
0
  }
5834
5835
0
  return res;
5836
0
}
5837
5838
0
int pr_fsio_lchown(const char *name, uid_t uid, gid_t gid) {
5839
0
  int res;
5840
0
  pr_fs_t *fs;
5841
5842
0
  if (name == NULL) {
5843
0
    errno = EINVAL;
5844
0
    return -1;
5845
0
  }
5846
5847
0
  fs = lookup_file_fs(name, NULL, FSIO_FILE_CHOWN);
5848
0
  if (fs == NULL) {
5849
0
    return -1;
5850
0
  }
5851
5852
  /* Find the first non-NULL custom lchown handler.  If there are none,
5853
   * use the system chown.
5854
   */
5855
0
  while (fs && fs->fs_next && !fs->lchown) {
5856
0
    fs = fs->fs_next;
5857
0
  }
5858
5859
0
  pr_trace_msg(trace_channel, 8, "using %s lchown() for path '%s'",
5860
0
    fs->fs_name, name);
5861
0
  res = (fs->lchown)(fs, name, uid, gid);
5862
0
  if (res == 0) {
5863
0
    pr_fs_clear_cache2(name);
5864
0
  }
5865
5866
0
  return res;
5867
0
}
5868
5869
int pr_fsio_lchown_with_error(pool *p, const char *path, uid_t uid, gid_t gid,
5870
0
    pr_error_t **err) {
5871
0
  int res;
5872
5873
0
  res = pr_fsio_lchown(path, uid, gid);
5874
0
  if (res < 0) {
5875
0
    int xerrno = errno;
5876
5877
0
    if (p != NULL &&
5878
0
        err != NULL) {
5879
0
      *err = pr_error_create(p, xerrno);
5880
0
      if (pr_error_explain_lchown(*err, path, uid, gid) < 0) {
5881
0
        pr_error_destroy(*err);
5882
0
        *err = NULL;
5883
0
      }
5884
0
    }
5885
5886
0
    errno = xerrno;
5887
0
  }
5888
5889
0
  return res;
5890
0
}
5891
5892
int pr_fsio_access(const char *path, int mode, uid_t uid, gid_t gid,
5893
0
    array_header *suppl_gids) {
5894
0
  pr_fs_t *fs;
5895
5896
0
  if (path == NULL) {
5897
0
    errno = EINVAL;
5898
0
    return -1;
5899
0
  }
5900
5901
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_ACCESS);
5902
0
  if (fs == NULL) {
5903
0
    return -1;
5904
0
  }
5905
5906
  /* Find the first non-NULL custom access handler.  If there are none,
5907
   * use the system access.
5908
   */
5909
0
  while (fs && fs->fs_next && !fs->access) {
5910
0
    fs = fs->fs_next;
5911
0
  }
5912
5913
0
  pr_trace_msg(trace_channel, 8, "using %s access() for path '%s'",
5914
0
    fs->fs_name, path);
5915
0
  return (fs->access)(fs, path, mode, uid, gid, suppl_gids);
5916
0
}
5917
5918
int pr_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
5919
0
    array_header *suppl_gids) {
5920
0
  pr_fs_t *fs;
5921
5922
0
  if (fh == NULL) {
5923
0
    errno = EINVAL;
5924
0
    return -1;
5925
0
  }
5926
5927
  /* Find the first non-NULL custom faccess handler.  If there are none,
5928
   * use the system faccess.
5929
   */
5930
0
  fs = fh->fh_fs;
5931
0
  while (fs && fs->fs_next && !fs->faccess) {
5932
0
    fs = fs->fs_next;
5933
0
  }
5934
5935
0
  pr_trace_msg(trace_channel, 8, "using %s faccess() for path '%s'",
5936
0
    fs->fs_name, fh->fh_path);
5937
0
  return (fs->faccess)(fh, mode, uid, gid, suppl_gids);
5938
0
}
5939
5940
0
int pr_fsio_utimes(const char *path, struct timeval *tvs) {
5941
0
  int res;
5942
0
  pr_fs_t *fs;
5943
5944
0
  if (path == NULL ||
5945
0
      tvs == NULL) {
5946
0
    errno = EINVAL;
5947
0
    return -1;
5948
0
  }
5949
5950
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_UTIMES);
5951
0
  if (fs == NULL) {
5952
0
    return -1;
5953
0
  }
5954
5955
  /* Find the first non-NULL custom utimes handler.  If there are none,
5956
   * use the system utimes.
5957
   */
5958
0
  while (fs && fs->fs_next && !fs->utimes) {
5959
0
    fs = fs->fs_next;
5960
0
  }
5961
5962
0
  pr_trace_msg(trace_channel, 8, "using %s utimes() for path '%s'",
5963
0
    fs->fs_name, path);
5964
0
  res = (fs->utimes)(fs, path, tvs);
5965
0
  if (res == 0) {
5966
0
    pr_fs_clear_cache2(path);
5967
0
  }
5968
5969
0
  return res;
5970
0
}
5971
5972
/* If the utimes(2) call fails because the process UID does not match the file
5973
 * UID, then check to see if the GIDs match (and that the file has group write
5974
 * permissions).
5975
 *
5976
 * This can be alleviated in two ways: a) if mod_cap is present, enable the
5977
 * CAP_FOWNER capability for the session, or b) use root privs.
5978
 */
5979
0
int pr_fsio_utimes_with_root(const char *path, struct timeval *tvs) {
5980
0
  int res, xerrno, matching_gid = FALSE;
5981
0
  struct stat st;
5982
5983
0
  res = pr_fsio_utimes(path, tvs);
5984
0
  xerrno = errno;
5985
5986
0
  if (res == 0) {
5987
0
    return 0;
5988
0
  }
5989
5990
  /* We only try these workarounds for EPERM. */
5991
0
  if (xerrno != EPERM) {
5992
0
    return res;
5993
0
  }
5994
5995
0
  pr_fs_clear_cache2(path);
5996
0
  if (pr_fsio_stat(path, &st) < 0) {
5997
0
    errno = xerrno;
5998
0
    return -1;
5999
0
  }
6000
6001
  /* Be sure to check the primary and all the supplemental groups to which
6002
   * this session belongs.
6003
   */
6004
0
  if (st.st_gid == session.gid) {
6005
0
    matching_gid = TRUE;
6006
6007
0
  } else if (session.gids != NULL) {
6008
0
    register unsigned int i;
6009
0
    gid_t *gids;
6010
6011
0
    gids = session.gids->elts;
6012
0
    for (i = 0; i < session.gids->nelts; i++) {
6013
0
      if (st.st_gid == gids[i]) {
6014
0
        matching_gid = TRUE;
6015
0
        break;
6016
0
      }
6017
0
    }
6018
0
  }
6019
6020
0
  if (matching_gid == TRUE &&
6021
0
      (st.st_mode & S_IWGRP)) {
6022
6023
    /* Try the utimes(2) call again, this time with root privs. */
6024
0
    pr_signals_block();
6025
0
    PRIVS_ROOT
6026
0
    res = pr_fsio_utimes(path, tvs);
6027
0
    PRIVS_RELINQUISH
6028
0
    pr_signals_unblock();
6029
6030
0
    if (res == 0) {
6031
0
      return 0;
6032
0
    }
6033
0
  }
6034
6035
0
  errno = xerrno;
6036
0
  return -1;
6037
0
}
6038
6039
0
int pr_fsio_futimes(pr_fh_t *fh, struct timeval *tvs) {
6040
0
  int res;
6041
0
  pr_fs_t *fs;
6042
6043
0
  if (fh == NULL ||
6044
0
      tvs == NULL) {
6045
0
    errno = EINVAL;
6046
0
    return -1;
6047
0
  }
6048
6049
  /* Find the first non-NULL custom futimes handler.  If there are none,
6050
   * use the system futimes.
6051
   */
6052
0
  fs = fh->fh_fs;
6053
0
  while (fs && fs->fs_next && !fs->futimes) {
6054
0
    fs = fs->fs_next;
6055
0
  }
6056
6057
0
  pr_trace_msg(trace_channel, 8, "using %s futimes() for path '%s'",
6058
0
    fs->fs_name, fh->fh_path);
6059
0
  res = (fs->futimes)(fh, fh->fh_fd, tvs);
6060
0
  if (res == 0) {
6061
0
    pr_fs_clear_cache2(fh->fh_path);
6062
0
  }
6063
6064
0
  return res;
6065
0
}
6066
6067
0
int pr_fsio_fsync(pr_fh_t *fh) {
6068
0
  int res;
6069
0
  pr_fs_t *fs;
6070
6071
0
  if (fh == NULL) {
6072
0
    errno = EINVAL;
6073
0
    return -1;
6074
0
  }
6075
6076
  /* Find the first non-NULL custom fsync handler.  If there are none,
6077
   * use the system fsync.
6078
   */
6079
0
  fs = fh->fh_fs;
6080
0
  while (fs && fs->fs_next && !fs->fsync) {
6081
0
    fs = fs->fs_next;
6082
0
  }
6083
6084
0
  pr_trace_msg(trace_channel, 8, "using %s fsync() for path '%s'",
6085
0
    fs->fs_name, fh->fh_path);
6086
0
  res = (fs->fsync)(fh, fh->fh_fd);
6087
0
  if (res == 0) {
6088
0
    pr_fs_clear_cache2(fh->fh_path);
6089
0
  }
6090
6091
0
  return res;
6092
0
}
6093
6094
0
const char *pr_fsio_realpath(pool *p, const char *path) {
6095
0
  const char *res;
6096
0
  pr_fs_t *fs;
6097
6098
0
  if (p == NULL ||
6099
0
      path == NULL) {
6100
0
    errno = EINVAL;
6101
0
    return NULL;
6102
0
  }
6103
6104
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_REALPATH);
6105
0
  if (fs == NULL) {
6106
0
    return NULL;
6107
0
  }
6108
6109
  /* Find the first non-NULL custom realpath handler.  If there are none,
6110
   * use the system realpath
6111
   */
6112
0
  while (fs && fs->fs_next && !fs->realpath) {
6113
0
    fs = fs->fs_next;
6114
0
  }
6115
6116
0
  pr_trace_msg(trace_channel, 8, "using %s realpath() for path '%s'",
6117
0
    fs->fs_name, path);
6118
0
  res = (fs->realpath)(fs, p, path);
6119
6120
0
  return res;
6121
0
}
6122
6123
ssize_t pr_fsio_getxattr(pool *p, const char *path, const char *name, void *val,
6124
0
    size_t valsz) {
6125
0
  ssize_t res;
6126
0
  pr_fs_t *fs;
6127
6128
0
  if (p == NULL ||
6129
0
      path == NULL ||
6130
0
      name == NULL) {
6131
0
    errno = EINVAL;
6132
0
    return -1;
6133
0
  }
6134
6135
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6136
0
    errno = ENOSYS;
6137
0
    return -1;
6138
0
  }
6139
6140
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_GETXATTR);
6141
0
  if (fs == NULL) {
6142
0
    return -1;
6143
0
  }
6144
6145
  /* Find the first non-NULL custom getxattr handler.  If there are none,
6146
   * use the system getxattr.
6147
   */
6148
0
  while (fs && fs->fs_next && !fs->getxattr) {
6149
0
    fs = fs->fs_next;
6150
0
  }
6151
6152
0
  pr_trace_msg(trace_channel, 8, "using %s getxattr() for path '%s'",
6153
0
    fs->fs_name, path);
6154
0
  res = (fs->getxattr)(p, fs, path, name, val, valsz);
6155
0
  return res;
6156
0
}
6157
6158
ssize_t pr_fsio_lgetxattr(pool *p, const char *path, const char *name,
6159
0
    void *val, size_t valsz) {
6160
0
  ssize_t res;
6161
0
  pr_fs_t *fs;
6162
6163
0
  if (p == NULL ||
6164
0
      path == NULL ||
6165
0
      name == NULL) {
6166
0
    errno = EINVAL;
6167
0
    return -1;
6168
0
  }
6169
6170
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6171
0
    errno = ENOSYS;
6172
0
    return -1;
6173
0
  }
6174
6175
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LGETXATTR);
6176
0
  if (fs == NULL) {
6177
0
    return -1;
6178
0
  }
6179
6180
  /* Find the first non-NULL custom lgetxattr handler.  If there are none,
6181
   * use the system lgetxattr.
6182
   */
6183
0
  while (fs && fs->fs_next && !fs->lgetxattr) {
6184
0
    fs = fs->fs_next;
6185
0
  }
6186
6187
0
  pr_trace_msg(trace_channel, 8, "using %s lgetxattr() for path '%s'",
6188
0
    fs->fs_name, path);
6189
0
  res = (fs->lgetxattr)(p, fs, path, name, val, valsz);
6190
0
  return res;
6191
0
}
6192
6193
ssize_t pr_fsio_fgetxattr(pool *p, pr_fh_t *fh, const char *name, void *val,
6194
0
    size_t valsz) {
6195
0
  ssize_t res;
6196
0
  pr_fs_t *fs;
6197
6198
0
  if (p == NULL ||
6199
0
      fh == NULL ||
6200
0
      name == NULL) {
6201
0
    errno = EINVAL;
6202
0
    return -1;
6203
0
  }
6204
6205
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6206
0
    errno = ENOSYS;
6207
0
    return -1;
6208
0
  }
6209
6210
  /* Find the first non-NULL custom fgetxattr handler.  If there are none,
6211
   * use the system fgetxattr.
6212
   */
6213
0
  fs = fh->fh_fs;
6214
0
  while (fs && fs->fs_next && !fs->fgetxattr) {
6215
0
    fs = fs->fs_next;
6216
0
  }
6217
6218
0
  pr_trace_msg(trace_channel, 8, "using %s fgetxattr() for path '%s'",
6219
0
    fs->fs_name, fh->fh_path);
6220
0
  res = (fs->fgetxattr)(p, fh, fh->fh_fd, name, val, valsz);
6221
0
  return res;
6222
0
}
6223
6224
0
int pr_fsio_listxattr(pool *p, const char *path, array_header **names) {
6225
0
  int res;
6226
0
  pr_fs_t *fs;
6227
6228
0
  if (p == NULL ||
6229
0
      path == NULL ||
6230
0
      names == NULL) {
6231
0
    errno = EINVAL;
6232
0
    return -1;
6233
0
  }
6234
6235
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6236
0
    errno = ENOSYS;
6237
0
    return -1;
6238
0
  }
6239
6240
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LISTXATTR);
6241
0
  if (fs == NULL) {
6242
0
    return -1;
6243
0
  }
6244
6245
  /* Find the first non-NULL custom listxattr handler.  If there are none,
6246
   * use the system listxattr.
6247
   */
6248
0
  while (fs && fs->fs_next && !fs->listxattr) {
6249
0
    fs = fs->fs_next;
6250
0
  }
6251
6252
0
  pr_trace_msg(trace_channel, 8, "using %s listxattr() for path '%s'",
6253
0
    fs->fs_name, path);
6254
0
  res = (fs->listxattr)(p, fs, path, names);
6255
0
  return res;
6256
0
}
6257
6258
0
int pr_fsio_llistxattr(pool *p, const char *path, array_header **names) {
6259
0
  int res;
6260
0
  pr_fs_t *fs;
6261
6262
0
  if (p == NULL ||
6263
0
      path == NULL ||
6264
0
      names == NULL) {
6265
0
    errno = EINVAL;
6266
0
    return -1;
6267
0
  }
6268
6269
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6270
0
    errno = ENOSYS;
6271
0
    return -1;
6272
0
  }
6273
6274
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LLISTXATTR);
6275
0
  if (fs == NULL) {
6276
0
    return -1;
6277
0
  }
6278
6279
  /* Find the first non-NULL custom llistxattr handler.  If there are none,
6280
   * use the system llistxattr.
6281
   */
6282
0
  while (fs && fs->fs_next && !fs->llistxattr) {
6283
0
    fs = fs->fs_next;
6284
0
  }
6285
6286
0
  pr_trace_msg(trace_channel, 8, "using %s llistxattr() for path '%s'",
6287
0
    fs->fs_name, path);
6288
0
  res = (fs->llistxattr)(p, fs, path, names);
6289
0
  return res;
6290
0
}
6291
6292
0
int pr_fsio_flistxattr(pool *p, pr_fh_t *fh, array_header **names) {
6293
0
  int res;
6294
0
  pr_fs_t *fs;
6295
6296
0
  if (p == NULL ||
6297
0
      fh == NULL ||
6298
0
      names == NULL) {
6299
0
    errno = EINVAL;
6300
0
    return -1;
6301
0
  }
6302
6303
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6304
0
    errno = ENOSYS;
6305
0
    return -1;
6306
0
  }
6307
6308
  /* Find the first non-NULL custom flistxattr handler.  If there are none,
6309
   * use the system flistxattr.
6310
   */
6311
0
  fs = fh->fh_fs;
6312
0
  while (fs && fs->fs_next && !fs->flistxattr) {
6313
0
    fs = fs->fs_next;
6314
0
  }
6315
6316
0
  pr_trace_msg(trace_channel, 8, "using %s flistxattr() for path '%s'",
6317
0
    fs->fs_name, fh->fh_path);
6318
0
  res = (fs->flistxattr)(p, fh, fh->fh_fd, names);
6319
0
  return res;
6320
0
}
6321
6322
0
int pr_fsio_removexattr(pool *p, const char *path, const char *name) {
6323
0
  int res;
6324
0
  pr_fs_t *fs;
6325
6326
0
  if (p == NULL ||
6327
0
      path == NULL ||
6328
0
      name == NULL) {
6329
0
    errno = EINVAL;
6330
0
    return -1;
6331
0
  }
6332
6333
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6334
0
    errno = ENOSYS;
6335
0
    return -1;
6336
0
  }
6337
6338
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_REMOVEXATTR);
6339
0
  if (fs == NULL) {
6340
0
    return -1;
6341
0
  }
6342
6343
  /* Find the first non-NULL custom removexattr handler.  If there are none,
6344
   * use the system removexattr.
6345
   */
6346
0
  while (fs && fs->fs_next && !fs->removexattr) {
6347
0
    fs = fs->fs_next;
6348
0
  }
6349
6350
0
  pr_trace_msg(trace_channel, 8, "using %s removexattr() for path '%s'",
6351
0
    fs->fs_name, path);
6352
0
  res = (fs->removexattr)(p, fs, path, name);
6353
0
  return res;
6354
0
}
6355
6356
0
int pr_fsio_lremovexattr(pool *p, const char *path, const char *name) {
6357
0
  int res;
6358
0
  pr_fs_t *fs;
6359
6360
0
  if (p == NULL ||
6361
0
      path == NULL ||
6362
0
      name == NULL) {
6363
0
    errno = EINVAL;
6364
0
    return -1;
6365
0
  }
6366
6367
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6368
0
    errno = ENOSYS;
6369
0
    return -1;
6370
0
  }
6371
6372
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LREMOVEXATTR);
6373
0
  if (fs == NULL) {
6374
0
    return -1;
6375
0
  }
6376
6377
  /* Find the first non-NULL custom lremovexattr handler.  If there are none,
6378
   * use the system lremovexattr.
6379
   */
6380
0
  while (fs && fs->fs_next && !fs->lremovexattr) {
6381
0
    fs = fs->fs_next;
6382
0
  }
6383
6384
0
  pr_trace_msg(trace_channel, 8, "using %s lremovexattr() for path '%s'",
6385
0
    fs->fs_name, path);
6386
0
  res = (fs->lremovexattr)(p, fs, path, name);
6387
0
  return res;
6388
0
}
6389
6390
0
int pr_fsio_fremovexattr(pool *p, pr_fh_t *fh, const char *name) {
6391
0
  int res;
6392
0
  pr_fs_t *fs;
6393
6394
0
  if (p == NULL ||
6395
0
      fh == NULL ||
6396
0
      name == NULL) {
6397
0
    errno = EINVAL;
6398
0
    return -1;
6399
0
  }
6400
6401
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6402
0
    errno = ENOSYS;
6403
0
    return -1;
6404
0
  }
6405
6406
  /* Find the first non-NULL custom fremovexattr handler.  If there are none,
6407
   * use the system fremovexattr.
6408
   */
6409
0
  fs = fh->fh_fs;
6410
0
  while (fs && fs->fs_next && !fs->fremovexattr) {
6411
0
    fs = fs->fs_next;
6412
0
  }
6413
6414
0
  pr_trace_msg(trace_channel, 8, "using %s fremovexattr() for path '%s'",
6415
0
    fs->fs_name, fh->fh_path);
6416
0
  res = (fs->fremovexattr)(p, fh, fh->fh_fd, name);
6417
0
  return res;
6418
0
}
6419
6420
int pr_fsio_setxattr(pool *p, const char *path, const char *name, void *val,
6421
0
    size_t valsz, int flags) {
6422
0
  int res;
6423
0
  pr_fs_t *fs;
6424
6425
0
  if (p == NULL ||
6426
0
      path == NULL ||
6427
0
      name == NULL) {
6428
0
    errno = EINVAL;
6429
0
    return -1;
6430
0
  }
6431
6432
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6433
0
    errno = ENOSYS;
6434
0
    return -1;
6435
0
  }
6436
6437
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_SETXATTR);
6438
0
  if (fs == NULL) {
6439
0
    return -1;
6440
0
  }
6441
6442
  /* Find the first non-NULL custom setxattr handler.  If there are none,
6443
   * use the system setxattr.
6444
   */
6445
0
  while (fs && fs->fs_next && !fs->setxattr) {
6446
0
    fs = fs->fs_next;
6447
0
  }
6448
6449
0
  pr_trace_msg(trace_channel, 8, "using %s setxattr() for path '%s'",
6450
0
    fs->fs_name, path);
6451
0
  res = (fs->setxattr)(p, fs, path, name, val, valsz, flags);
6452
0
  return res;
6453
0
}
6454
6455
int pr_fsio_lsetxattr(pool *p, const char *path, const char *name, void *val,
6456
0
    size_t valsz, int flags) {
6457
0
  int res;
6458
0
  pr_fs_t *fs;
6459
6460
0
  if (p == NULL ||
6461
0
      path == NULL ||
6462
0
      name == NULL) {
6463
0
    errno = EINVAL;
6464
0
    return -1;
6465
0
  }
6466
6467
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6468
0
    errno = ENOSYS;
6469
0
    return -1;
6470
0
  }
6471
6472
0
  fs = lookup_file_fs(path, NULL, FSIO_FILE_LSETXATTR);
6473
0
  if (fs == NULL) {
6474
0
    return -1;
6475
0
  }
6476
6477
  /* Find the first non-NULL custom lsetxattr handler.  If there are none,
6478
   * use the system lsetxattr.
6479
   */
6480
0
  while (fs && fs->fs_next && !fs->lsetxattr) {
6481
0
    fs = fs->fs_next;
6482
0
  }
6483
6484
0
  pr_trace_msg(trace_channel, 8, "using %s lsetxattr() for path '%s'",
6485
0
    fs->fs_name, path);
6486
0
  res = (fs->lsetxattr)(p, fs, path, name, val, valsz, flags);
6487
0
  return res;
6488
0
}
6489
6490
int pr_fsio_fsetxattr(pool *p, pr_fh_t *fh, const char *name, void *val,
6491
0
    size_t valsz, int flags) {
6492
0
  int res;
6493
0
  pr_fs_t *fs;
6494
6495
0
  if (p == NULL ||
6496
0
      fh == NULL ||
6497
0
      name == NULL) {
6498
0
    errno = EINVAL;
6499
0
    return -1;
6500
0
  }
6501
6502
0
  if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) {
6503
0
    errno = ENOSYS;
6504
0
    return -1;
6505
0
  }
6506
6507
  /* Find the first non-NULL custom fsetxattr handler.  If there are none,
6508
   * use the system fsetxattr.
6509
   */
6510
0
  fs = fh->fh_fs;
6511
0
  while (fs && fs->fs_next && !fs->fsetxattr) {
6512
0
    fs = fs->fs_next;
6513
0
  }
6514
6515
0
  pr_trace_msg(trace_channel, 8, "using %s fsetxattr() for path '%s'",
6516
0
    fs->fs_name, fh->fh_path);
6517
0
  res = (fs->fsetxattr)(p, fh, fh->fh_fd, name, val, valsz, flags);
6518
0
  return res;
6519
0
}
6520
6521
/* If the wrapped chroot() function succeeds (e.g. returns 0), then all
6522
 * pr_fs_ts currently registered in the fs_map will have their paths
6523
 * rewritten to reflect the new root.
6524
 */
6525
0
int pr_fsio_chroot(const char *path) {
6526
0
  int res = 0, xerrno = 0;
6527
0
  pr_fs_t *fs;
6528
6529
0
  if (path == NULL) {
6530
0
    errno = EINVAL;
6531
0
    return -1;
6532
0
  }
6533
6534
0
  fs = lookup_dir_fs(path, FSIO_DIR_CHROOT);
6535
0
  if (fs == NULL) {
6536
0
    return -1;
6537
0
  }
6538
6539
  /* Find the first non-NULL custom chroot handler.  If there are none,
6540
   * use the system chroot.
6541
   */
6542
0
  while (fs && fs->fs_next && !fs->chroot) {
6543
0
    fs = fs->fs_next;
6544
0
  }
6545
6546
0
  pr_trace_msg(trace_channel, 8, "using %s chroot() for path '%s'",
6547
0
    fs->fs_name, path);
6548
0
  res = (fs->chroot)(fs, path);
6549
0
  xerrno = errno;
6550
6551
0
  if (res == 0) {
6552
0
    unsigned int iter_start = 0;
6553
6554
    /* The filesystem handles in fs_map need to be readjusted to the new root.
6555
     */
6556
0
    register unsigned int i = 0;
6557
0
    pool *map_pool = make_sub_pool(permanent_pool);
6558
0
    array_header *new_map = make_array(map_pool, 0, sizeof(pr_fs_t *));
6559
0
    pr_fs_t **fs_objs = NULL;
6560
6561
0
    pr_pool_tag(map_pool, "FSIO Map Pool");
6562
6563
0
    if (fs_map) {
6564
0
      fs_objs = (pr_fs_t **) fs_map->elts;
6565
0
    }
6566
6567
0
    if (fs != root_fs) {
6568
0
      if (strncmp(fs->fs_path, path, strlen(path)) == 0) {
6569
0
        memmove(fs->fs_path, fs->fs_path + strlen(path),
6570
0
          strlen(fs->fs_path) - strlen(path) + 1);
6571
0
      }
6572
6573
0
      *((pr_fs_t **) push_array(new_map)) = fs;
6574
0
      iter_start = 1;
6575
0
    }
6576
6577
0
    for (i = iter_start; i < (fs_map ? fs_map->nelts : 0); i++) {
6578
0
      pr_fs_t *tmpfs = fs_objs[i];
6579
6580
      /* The memory for this field has already been allocated, so futzing
6581
       * with it like this should be fine.  Watch out for any paths that
6582
       * may be different, e.g. added manually, not through pr_register_fs().
6583
       * Any absolute paths that are outside of the chroot path are discarded.
6584
       * Deferred-resolution paths (eg "~" paths) and relative paths are kept.
6585
       */
6586
6587
0
      if (strncmp(tmpfs->fs_path, path, strlen(path)) == 0) {
6588
0
        pr_fs_t *next;
6589
6590
0
        memmove(tmpfs->fs_path, tmpfs->fs_path + strlen(path),
6591
0
          strlen(tmpfs->fs_path) - strlen(path) + 1);
6592
6593
        /* Need to do this for any stacked FSs as well. */
6594
0
        next = tmpfs->fs_next;
6595
0
        while (next != NULL) {
6596
0
          pr_signals_handle();
6597
6598
0
          memmove(next->fs_path, next->fs_path + strlen(path),
6599
0
            strlen(next->fs_path) - strlen(path) + 1);
6600
6601
0
          next = next->fs_next;
6602
0
        }
6603
0
      }
6604
6605
      /* Add this FS to the new fs_map. */
6606
0
      *((pr_fs_t **) push_array(new_map)) = tmpfs;
6607
0
    }
6608
6609
    /* Sort the new map */
6610
0
    qsort(new_map->elts, new_map->nelts, sizeof(pr_fs_t *), fs_cmp);
6611
6612
    /* Destroy the old map */
6613
0
    if (fs_map != NULL) {
6614
0
      destroy_pool(fs_map->pool);
6615
0
    }
6616
6617
0
    fs_map = new_map;
6618
0
    chk_fs_map = TRUE;
6619
0
  }
6620
6621
0
  errno = xerrno;
6622
0
  return res;
6623
0
}
6624
6625
0
int pr_fsio_chroot_with_error(pool *p, const char *path, pr_error_t **err) {
6626
0
  int res;
6627
6628
0
  res = pr_fsio_chroot(path);
6629
0
  if (res < 0) {
6630
0
    int xerrno = errno;
6631
6632
0
    if (p != NULL &&
6633
0
        err != NULL) {
6634
0
      *err = pr_error_create(p, xerrno);
6635
0
      if (pr_error_explain_chroot(*err, path) < 0) {
6636
0
        pr_error_destroy(*err);
6637
0
        *err = NULL;
6638
0
      }
6639
0
    }
6640
6641
0
    errno = xerrno;
6642
0
  }
6643
6644
0
  return res;
6645
0
}
6646
6647
0
char *pr_fsio_getpipebuf(pool *p, int fd, long *bufsz) {
6648
0
  char *buf = NULL;
6649
0
  long buflen;
6650
6651
0
  if (p == NULL) {
6652
0
    errno = EINVAL;
6653
0
    return NULL;
6654
0
  }
6655
6656
0
  if (fd < 0) {
6657
0
    errno = EBADF;
6658
0
    return NULL;
6659
0
  }
6660
6661
0
#if defined(PIPE_BUF)
6662
0
  buflen = PIPE_BUF;
6663
6664
#elif defined(HAVE_FPATHCONF)
6665
  /* Some platforms do not define a PIPE_BUF constant.  For them, we need
6666
   * to use fpathconf(2), if available.
6667
   */
6668
  buflen = fpathconf(fd, _PC_PIPE_BUF);
6669
  if (buflen < 0) {
6670
    return NULL;
6671
  }
6672
6673
#else
6674
  errno = ENOSYS;
6675
  return NULL;
6676
#endif
6677
6678
0
  if (bufsz != NULL) {
6679
0
    *bufsz = buflen;
6680
0
  }
6681
6682
0
  buf = palloc(p, buflen);
6683
0
  return buf;
6684
0
}
6685
6686
0
char *pr_fsio_gets(char *buf, size_t size, pr_fh_t *fh) {
6687
0
  char *bp = NULL;
6688
0
  int toread = 0;
6689
0
  pr_buffer_t *pbuf = NULL;
6690
6691
0
  if (buf == NULL ||
6692
0
      fh == NULL ||
6693
0
      size == 0) {
6694
0
    errno = EINVAL;
6695
0
    return NULL;
6696
0
  }
6697
6698
0
  if (fh->fh_buf == NULL) {
6699
0
    size_t bufsz;
6700
6701
    /* Conscientious callers who want the optimal IO on the file should
6702
     * set the fh->fh_iosz hint.
6703
     */
6704
0
    bufsz = fh->fh_iosz ? fh->fh_iosz : PR_TUNABLE_BUFFER_SIZE;
6705
6706
0
    fh->fh_buf = pcalloc(fh->fh_pool, sizeof(pr_buffer_t));
6707
0
    fh->fh_buf->buf = fh->fh_buf->current = pcalloc(fh->fh_pool, bufsz);
6708
0
    fh->fh_buf->remaining = fh->fh_buf->buflen = bufsz;
6709
0
  }
6710
6711
0
  pbuf = fh->fh_buf;
6712
0
  bp = buf;
6713
6714
0
  while (size) {
6715
0
    pr_signals_handle();
6716
6717
0
    if (pbuf->current == NULL ||
6718
0
        pbuf->remaining == pbuf->buflen) { /* empty buffer */
6719
6720
0
      toread = pr_fsio_read(fh, pbuf->buf, pbuf->buflen);
6721
0
      if (toread <= 0) {
6722
0
        if (bp != buf) {
6723
0
          *bp = '\0';
6724
0
          return buf;
6725
0
        }
6726
6727
0
        return NULL;
6728
0
      }
6729
6730
0
      pbuf->remaining = pbuf->buflen - toread;
6731
0
      pbuf->current = pbuf->buf;
6732
6733
0
    } else {
6734
0
      toread = pbuf->buflen - pbuf->remaining;
6735
0
    }
6736
6737
    /* TODO: Improve the efficiency of this copy by using a strnchr(3)
6738
     * scan to find the next LF, and then a memmove(2) to do the copy.
6739
     */
6740
0
    while (size &&
6741
0
           toread > 0 &&
6742
0
           *pbuf->current != '\n' &&
6743
0
           toread--) {
6744
0
      pr_signals_handle();
6745
6746
0
      *bp++ = *pbuf->current++;
6747
0
      size--;
6748
0
      pbuf->remaining++;
6749
0
    }
6750
6751
0
    if (size &&
6752
0
        toread &&
6753
0
        *pbuf->current == '\n') {
6754
0
      size--;
6755
0
      toread--;
6756
0
      *bp++ = *pbuf->current++;
6757
0
      pbuf->remaining++;
6758
0
      break;
6759
0
    }
6760
6761
0
    if (!toread) {
6762
0
      pbuf->current = NULL;
6763
0
    }
6764
0
  }
6765
6766
0
  *bp = '\0';
6767
0
  return buf;
6768
0
}
6769
6770
/* pr_fsio_getline() is an fgets() with backslash-newline stripping, copied from
6771
 * Wietse Venema's tcpwrapppers-7.6 code.  The extra *lineno argument is
6772
 * needed, at the moment, to properly track which line of the configuration
6773
 * file is being read in, so that errors can be reported with line numbers
6774
 * correctly.
6775
 */
6776
char *pr_fsio_getline(char *buf, size_t buflen, pr_fh_t *fh,
6777
0
    unsigned int *lineno) {
6778
0
  int inlen;
6779
0
  char *start;
6780
6781
0
  if (buf == NULL ||
6782
0
      fh == NULL ||
6783
0
      buflen == 0) {
6784
0
    errno = EINVAL;
6785
0
    return NULL;
6786
0
  }
6787
6788
0
  start = buf;
6789
0
  while (pr_fsio_gets(buf, buflen, fh) != NULL) {
6790
0
    pr_signals_handle();
6791
6792
0
    inlen = strlen(buf);
6793
6794
0
    if (inlen >= 1) {
6795
0
      if (buf[inlen - 1] == '\n') {
6796
0
        if (lineno != NULL) {
6797
0
          (*lineno)++;
6798
0
        }
6799
6800
0
        if (inlen >= 2 && buf[inlen - 2] == '\\') {
6801
0
          char *bufp;
6802
6803
0
          inlen -= 2;
6804
6805
          /* Watch for commented lines when handling line continuations.
6806
           * Advance past any leading whitespace, to see if the first
6807
           * non-whitespace character is the comment character.
6808
           */
6809
0
          for (bufp = buf; *bufp && PR_ISSPACE(*bufp); bufp++) {
6810
0
          }
6811
6812
0
          if (*bufp == '#') {
6813
0
            continue;
6814
0
          }
6815
6816
0
        } else {
6817
0
          return start;
6818
0
        }
6819
0
      }
6820
0
    }
6821
6822
    /* Be careful of reading too much. */
6823
0
    if (buflen - inlen == 0) {
6824
0
      return buf;
6825
0
    }
6826
6827
0
    buf += inlen;
6828
0
    buflen -= inlen;
6829
0
    buf[0] = 0;
6830
0
  }
6831
6832
0
  return (buf > start ? start : NULL);
6833
0
}
6834
6835
0
#define FSIO_MAX_FD_COUNT   1024
6836
6837
0
void pr_fs_close_extra_fds(void) {
6838
0
  register unsigned int i;
6839
0
  long nfiles = 0;
6840
0
  struct rlimit rlim;
6841
6842
  /* Close any but the big three open fds.
6843
   *
6844
   * First, use getrlimit() to obtain the maximum number of open files
6845
   * for this process -- then close that number.
6846
   */
6847
0
#if defined(RLIMIT_NOFILE) || defined(RLIMIT_OFILE)
6848
0
# if defined(RLIMIT_NOFILE)
6849
0
  if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
6850
# elif defined(RLIMIT_OFILE)
6851
  if (getrlimit(RLIMIT_OFILE, &rlim) < 0) {
6852
# endif
6853
    /* Ignore ENOSYS (and EPERM, since some libc's use this as ENOSYS); pick
6854
     * some arbitrary high number.
6855
     */
6856
0
    nfiles = FSIO_MAX_FD_COUNT;
6857
6858
0
  } else {
6859
0
    nfiles = rlim.rlim_max;
6860
0
  }
6861
6862
#else /* no RLIMIT_NOFILE or RLIMIT_OFILE */
6863
   nfiles = FSIO_MAX_FD_COUNT;
6864
#endif
6865
6866
  /* Yes, using a long for the nfiles variable is not quite kosher; it should
6867
   * be an unsigned type, otherwise a large limit (say, RLIMIT_INFINITY)
6868
   * might overflow the data type.  In that case, though, we want to know
6869
   * about it -- and using a signed type, we will know if the overflowed
6870
   * value is a negative number.  Chances are we do NOT want to be closing
6871
   * fds whose value is as high as they can possibly get; that's too many
6872
   * fds to iterate over.  Long story short, using a long int is just fine.
6873
   * (Plus it makes mod_exec work on Mac OSX 10.4; without this tweak,
6874
   * mod_exec's forked processes never return/exit.)
6875
   */
6876
6877
0
  if (nfiles < 0 ||
6878
0
      nfiles > FSIO_MAX_FD_COUNT) {
6879
0
    nfiles = FSIO_MAX_FD_COUNT;
6880
0
  }
6881
6882
  /* Close the "non-standard" file descriptors. */
6883
0
  for (i = 3; i < nfiles; i++) {
6884
    /* This is a potentially long-running loop, so handle signals. */
6885
0
    pr_signals_handle();
6886
0
    (void) close((int) i);
6887
0
  }
6888
0
}
6889
6890
/* Be generous in the maximum allowed number of dup fds, in our search for
6891
 * one that is outside the big three.
6892
 *
6893
 * In theory, this should be a runtime lookup using getdtablesize(2), being
6894
 * sure to handle the ENOSYS case (for older systems).
6895
 */
6896
0
#define FSIO_MAX_DUPFDS   512
6897
6898
/* The main three fds (stdin, stdout, stderr) need to be protected, reserved
6899
 * for use.  This function uses dup(2) to open new fds on the given fd
6900
 * until the new fd is not one of the big three.
6901
 */
6902
0
int pr_fs_get_usable_fd(int fd) {
6903
0
  register int i;
6904
0
  int fdi, dup_fds[FSIO_MAX_DUPFDS], n;
6905
6906
0
  if (fd > STDERR_FILENO) {
6907
0
    return fd;
6908
0
  }
6909
6910
0
  memset(dup_fds, -1, sizeof(dup_fds));
6911
0
  i = 0;
6912
0
  n = -1;
6913
6914
0
  fdi = fd;
6915
0
  while (i < FSIO_MAX_DUPFDS) {
6916
0
    pr_signals_handle();
6917
6918
0
    dup_fds[i] = dup(fdi);
6919
0
    if (dup_fds[i] < 0) {
6920
0
      register int j;
6921
0
      int xerrno  = errno;
6922
6923
      /* Need to clean up any previously opened dups as well. */
6924
0
      for (j = 0; j <= i; j++) {
6925
0
        close(dup_fds[j]);
6926
0
        dup_fds[j] = -1;
6927
0
      }
6928
6929
0
      errno = xerrno;
6930
0
      return -1;
6931
0
    }
6932
6933
0
    if (dup_fds[i] <= STDERR_FILENO) {
6934
      /* Continue searching for an open fd that isn't 0, 1, or 2. */
6935
0
      fdi = dup_fds[i];
6936
0
      i++;
6937
0
      continue;
6938
0
    }
6939
6940
0
    n = i;
6941
0
    fdi = dup_fds[n];
6942
0
    break;
6943
0
  }
6944
6945
  /* If n is -1, we reached the max number of dups without finding an
6946
   * open one.  Hard to imagine this happening, but catch the case anyway.
6947
   */
6948
0
  if (n == -1) {
6949
    /* Free up the fds we opened in our search. */
6950
0
    for (i = 0; i < FSIO_MAX_DUPFDS; i++) {
6951
0
      if (dup_fds[i] >= 0) {
6952
0
        close(dup_fds[i]);
6953
0
        dup_fds[i] = -1;
6954
0
      }
6955
0
    }
6956
6957
0
    errno = EPERM;
6958
0
    return -1;
6959
0
  }
6960
6961
  /* Free up the fds we opened in our search. */
6962
0
  for (i = 0; i < n; i++) {
6963
0
    (void) close(dup_fds[i]);
6964
0
    dup_fds[i] = -1;
6965
0
  }
6966
6967
0
  return fdi;
6968
0
}
6969
6970
0
int pr_fs_get_usable_fd2(int *fd) {
6971
0
  int new_fd = -1, res = 0;
6972
6973
0
  if (fd == NULL) {
6974
0
    errno = EINVAL;
6975
0
    return -1;
6976
0
  }
6977
6978
0
  if (*fd > STDERR_FILENO) {
6979
    /* No need to obtain a different fd; the given one is already not one
6980
     * of the big three.
6981
     */
6982
0
    return 0;
6983
0
  }
6984
6985
0
  new_fd = pr_fs_get_usable_fd(*fd);
6986
0
  if (new_fd >= 0) {
6987
0
    (void) close(*fd);
6988
0
    *fd = new_fd;
6989
6990
0
  } else {
6991
0
    res = -1;
6992
0
  }
6993
6994
0
  return res;
6995
0
}
6996
6997
/* Simple multiplication and division doesn't work with very large
6998
 * filesystems (overflows 32 bits).  This code should handle it.
6999
 *
7000
 * Note that this returns a size in KB, not bytes.
7001
 */
7002
0
static off_t get_fs_size(size_t nblocks, size_t blocksz) {
7003
0
  off_t bl_lo, bl_hi;
7004
0
  off_t res_lo, res_hi, tmp;
7005
0
7006
0
  bl_lo = nblocks & 0x0000ffff;
7007
0
  bl_hi = nblocks & 0xffff0000;
7008
0
7009
0
  tmp = (bl_hi >> 16) * blocksz;
7010
0
  res_hi = tmp & 0xffff0000;
7011
0
  res_lo = (tmp & 0x0000ffff) << 16;
7012
0
  res_lo += bl_lo * blocksz;
7013
0
7014
0
  if (res_hi & 0xfc000000) {
7015
0
    /* Overflow */
7016
0
    return 0;
7017
0
  }
7018
0
7019
0
  return (res_lo >> 10) | (res_hi << 6);
7020
0
}
7021
7022
0
static int fs_getsize(int fd, char *path, off_t *fs_size) {
7023
0
  int res = -1;
7024
7025
0
# if defined(HAVE_SYS_STATVFS_H)
7026
7027
#  if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && \
7028
    defined(SOLARIS2) && !defined(SOLARIS2_5_1) && !defined(SOLARIS2_6) && \
7029
    !defined(SOLARIS2_7)
7030
  /* Note: somewhere along the way, Sun decided that the prototype for
7031
   * its statvfs64(2) function would include a statvfs64_t rather than
7032
   * struct statvfs64.  In 2.6 and 2.7, it's struct statvfs64, and
7033
   * in 8, 9 it's statvfs64_t.  This should silence compiler warnings.
7034
   * (The statvfs_t will be redefined to a statvfs64_t as appropriate on
7035
   * LFS systems).
7036
   */
7037
  statvfs_t fs;
7038
#  else
7039
0
  struct statvfs fs;
7040
0
#  endif /* LFS && !Solaris 2.5.1 && !Solaris 2.6 && !Solaris 2.7 */
7041
7042
0
  if (fs_size == NULL) {
7043
0
    errno = EINVAL;
7044
0
    return -1;
7045
0
  }
7046
7047
0
  if (path != NULL) {
7048
0
    pr_trace_msg(trace_channel, 18, "using statvfs() on '%s'", path);
7049
7050
0
  } else {
7051
0
    pr_trace_msg(trace_channel, 18, "using statvfs() on fd %d", fd);
7052
0
  }
7053
7054
0
  if (path != NULL) {
7055
0
    res = statvfs(path, &fs);
7056
7057
0
  } else {
7058
0
    res = fstatvfs(fd, &fs);
7059
0
  }
7060
7061
0
  if (res < 0) {
7062
0
    int xerrno = errno;
7063
7064
0
    if (path != NULL) {
7065
0
      pr_trace_msg(trace_channel, 3, "statvfs() error using '%s': %s",
7066
0
        path, strerror(xerrno));
7067
7068
0
    } else {
7069
0
      pr_trace_msg(trace_channel, 3, "statvfs() error using fd %d: %s",
7070
0
        fd, strerror(xerrno));
7071
0
    }
7072
7073
0
    errno = xerrno;
7074
0
    return -1;
7075
0
  }
7076
7077
  /* The get_fs_size() function is only useful for 32-bit numbers;
7078
   * if either of our two values are in datatypes larger than 4 bytes,
7079
   * we'll use typecasting.
7080
   */
7081
0
  if (sizeof(fs.f_bavail) > 4 ||
7082
0
      sizeof(fs.f_frsize) > 4) {
7083
7084
    /* In order to return a size in KB, as get_fs_size() does, we need
7085
     * to divide by 1024.
7086
     */
7087
0
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7088
7089
0
  } else {
7090
0
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7091
0
  }
7092
7093
0
  res = 0;
7094
7095
# elif defined(HAVE_SYS_VFS_H)
7096
  struct statfs fs;
7097
7098
  if (fs_size == NULL) {
7099
    errno = EINVAL;
7100
    return -1;
7101
  }
7102
7103
  if (path != NULL) {
7104
    pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7105
7106
  } else {
7107
    pr_trace_msg(trace_channel, 18, "using statfs() on fd %d", fd);
7108
  }
7109
7110
  if (path != NULL) {
7111
    res = statfs(path, &fs);
7112
7113
  } else {
7114
    res = fstatfs(fd, &fs);
7115
  }
7116
7117
  if (res < 0) {
7118
    int xerrno = errno;
7119
7120
    if (path != NULL) {
7121
      pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7122
        path, strerror(xerrno));
7123
7124
    } else {
7125
      pr_trace_msg(trace_channel, 3, "statfs() error using fd %d: %s",
7126
        fd, strerror(xerrno));
7127
    }
7128
7129
    errno = xerrno;
7130
    return -1;
7131
  }
7132
7133
  /* The get_fs_size() function is only useful for 32-bit numbers;
7134
   * if either of our two values are in datatypes larger than 4 bytes,
7135
   * we'll use typecasting.
7136
   */
7137
  if (sizeof(fs.f_bavail) > 4 ||
7138
      sizeof(fs.f_frsize) > 4) {
7139
7140
    /* In order to return a size in KB, as get_fs_size() does, we need
7141
     * to divide by 1024.
7142
     */
7143
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7144
7145
  } else {
7146
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7147
  }
7148
7149
  res = 0;
7150
7151
# elif defined(HAVE_STATFS)
7152
  struct statfs fs;
7153
7154
  if (fs_size == NULL) {
7155
    errno = EINVAL;
7156
    return -1;
7157
  }
7158
7159
  if (path != NULL) {
7160
    pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7161
7162
  } else {
7163
    pr_trace_msg(trace_channel, 18, "using statfs() on fd %d", fd);
7164
  }
7165
7166
  if (path != NULL) {
7167
    res = statfs(path, &fs);
7168
7169
  } else {
7170
    res = fstatfs(fd, &fs);
7171
  }
7172
7173
  if (res < 0) {
7174
    int xerrno = errno;
7175
7176
    if (path != NULL) {
7177
      pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7178
        path, strerror(xerrno));
7179
7180
    } else {
7181
      pr_trace_msg(trace_channel, 3, "statfs() error using fd %d: %s",
7182
        fd, strerror(xerrno));
7183
    }
7184
7185
    errno = xerrno;
7186
    return -1;
7187
  }
7188
7189
  /* The get_fs_size() function is only useful for 32-bit numbers;
7190
   * if either of our two values are in datatypes larger than 4 bytes,
7191
   * we'll use typecasting.
7192
   */
7193
  if (sizeof(fs.f_bavail) > 4 ||
7194
      sizeof(fs.f_frsize) > 4) {
7195
7196
    /* In order to return a size in KB, as get_fs_size() does, we need
7197
     * to divide by 1024.
7198
     */
7199
    *fs_size = (((off_t) fs.f_bavail * (off_t) fs.f_frsize) / 1024);
7200
7201
  } else {
7202
    *fs_size = get_fs_size(fs.f_bavail, fs.f_frsize);
7203
  }
7204
7205
  res = 0;
7206
7207
# else
7208
  errno = ENOSYS:
7209
  res = -1;
7210
# endif /* !HAVE_STATFS && !HAVE_SYS_STATVFS && !HAVE_SYS_VFS */
7211
7212
0
  return res;
7213
0
}
7214
7215
#if defined(HAVE_STATFS) || defined(HAVE_SYS_STATVFS_H) || \
7216
  defined(HAVE_SYS_VFS_H)
7217
0
off_t pr_fs_getsize(char *path) {
7218
0
  int res;
7219
0
  off_t fs_size;
7220
7221
0
  res = pr_fs_getsize2(path, &fs_size);
7222
0
  if (res < 0) {
7223
0
    errno = EINVAL;
7224
0
    fs_size = -1;
7225
0
  }
7226
7227
0
  return fs_size;
7228
0
}
7229
#endif /* !HAVE_STATFS && !HAVE_SYS_STATVFS && !HAVE_SYS_VFS */
7230
7231
/* Returns the size in KB via the `fs_size' argument. */
7232
0
int pr_fs_getsize2(char *path, off_t *fs_size) {
7233
0
  return fs_getsize(-1, path, fs_size);
7234
0
}
7235
7236
0
int pr_fs_fgetsize(int fd, off_t *fs_size) {
7237
0
  return fs_getsize(fd, NULL, fs_size);
7238
0
}
7239
7240
0
void pr_fs_fadvise(int fd, off_t offset, off_t len, int advice) {
7241
#if defined(HAVE_POSIX_ADVISE)
7242
  int res, posix_advice;
7243
  const char *advice_str;
7244
7245
  /* Convert from our advice values to the ones from the header; the
7246
   * indirection is needed for platforms which do not provide posix_fadvise(3).
7247
   */
7248
  switch (advice) {
7249
    case PR_FS_FADVISE_NORMAL:
7250
      advice_str = "NORMAL";
7251
      posix_advice = POSIX_FADV_NORMAL;
7252
      break;
7253
7254
    case PR_FS_FADVISE_RANDOM:
7255
      advice_str = "RANDOM";
7256
      posix_advice = POSIX_FADV_RANDOM;
7257
      break;
7258
7259
    case PR_FS_FADVISE_SEQUENTIAL:
7260
      advice_str = "SEQUENTIAL";
7261
      posix_advice = POSIX_FADV_SEQUENTIAL;
7262
      break;
7263
7264
    case PR_FS_FADVISE_WILLNEED:
7265
      advice_str = "WILLNEED";
7266
      posix_advice = POSIX_FADV_WILLNEED;
7267
      break;
7268
7269
    case PR_FS_FADVISE_DONTNEED:
7270
      advice_str = "DONTNEED";
7271
      posix_advice = POSIX_FADV_DONTNEED;
7272
      break;
7273
7274
    case PR_FS_FADVISE_NOREUSE:
7275
      advice_str = "NOREUSE";
7276
      posix_advice = POSIX_FADV_NOREUSE;
7277
      break;
7278
7279
    default:
7280
      pr_trace_msg(trace_channel, 9,
7281
        "unknown/unsupported advice: %d", advice);
7282
      return;
7283
  }
7284
7285
  res = posix_fadvise(fd, offset, len, posix_advice);
7286
  if (res < 0) {
7287
    pr_trace_msg(trace_channel, 9,
7288
      "posix_fadvise() error on fd %d (off %" PR_LU ", len %" PR_LU ", "
7289
      "advice %s): %s", fd, (pr_off_t) offset, (pr_off_t) len, advice_str,
7290
      strerror(errno));
7291
  }
7292
#endif
7293
0
}
7294
7295
int pr_fs_have_access(struct stat *st, int mode, uid_t uid, gid_t gid,
7296
0
    array_header *suppl_gids) {
7297
0
  mode_t mask;
7298
7299
0
  if (st == NULL) {
7300
0
    errno = EINVAL;
7301
0
    return -1;
7302
0
  }
7303
7304
  /* Root always succeeds for reads/writes. */
7305
0
  if (uid == PR_ROOT_UID &&
7306
0
      mode != X_OK) {
7307
0
    return 0;
7308
0
  }
7309
7310
  /* Initialize mask to reflect the permission bits that are applicable for
7311
   * the given user. mask contains the user-bits if the user ID equals the
7312
   * ID of the file owner. mask contains the group bits if the group ID
7313
   * belongs to the group of the file. mask will always contain the other
7314
   * bits of the permission bits.
7315
   */
7316
0
  mask = S_IROTH|S_IWOTH|S_IXOTH;
7317
7318
0
  if (st->st_uid == uid) {
7319
0
    mask |= S_IRUSR|S_IWUSR|S_IXUSR;
7320
0
  }
7321
7322
  /* Check the current group, as well as all supplementary groups.
7323
   * Fortunately, we have this information cached, so accessing it is
7324
   * almost free.
7325
   */
7326
0
  if (st->st_gid == gid) {
7327
0
    mask |= S_IRGRP|S_IWGRP|S_IXGRP;
7328
7329
0
  } else {
7330
0
    if (suppl_gids != NULL) {
7331
0
      register unsigned int i = 0;
7332
7333
0
      for (i = 0; i < suppl_gids->nelts; i++) {
7334
0
        if (st->st_gid == ((gid_t *) suppl_gids->elts)[i]) {
7335
0
          mask |= S_IRGRP|S_IWGRP|S_IXGRP;
7336
0
          break;
7337
0
        }
7338
0
      }
7339
0
    }
7340
0
  }
7341
7342
0
  mask &= st->st_mode;
7343
7344
  /* Perform requested access checks. */
7345
0
  if (mode & R_OK) {
7346
0
    if (!(mask & (S_IRUSR|S_IRGRP|S_IROTH))) {
7347
0
      errno = EACCES;
7348
0
      return -1;
7349
0
    }
7350
0
  }
7351
7352
0
  if (mode & W_OK) {
7353
0
    if (!(mask & (S_IWUSR|S_IWGRP|S_IWOTH))) {
7354
0
      errno = EACCES;
7355
0
      return -1;
7356
0
    }
7357
0
  }
7358
7359
0
  if (mode & X_OK) {
7360
0
    if (!(mask & (S_IXUSR|S_IXGRP|S_IXOTH))) {
7361
0
      errno = EACCES;
7362
0
      return -1;
7363
0
    }
7364
0
  }
7365
7366
  /* F_OK already checked by checking the return value of stat. */
7367
0
  return 0;
7368
0
}
7369
7370
0
int pr_fs_is_nfs(const char *path) {
7371
0
#if defined(HAVE_STATFS_F_TYPE) || defined(HAVE_STATFS_F_FSTYPENAME)
7372
0
  struct statfs fs;
7373
0
  int res = FALSE;
7374
7375
0
  if (path == NULL) {
7376
0
    errno = EINVAL;
7377
0
    return -1;
7378
0
  }
7379
7380
0
  pr_trace_msg(trace_channel, 18, "using statfs() on '%s'", path);
7381
0
  if (statfs(path, &fs) < 0) {
7382
0
    int xerrno = errno;
7383
7384
0
    pr_trace_msg(trace_channel, 3, "statfs() error using '%s': %s",
7385
0
      path, strerror(xerrno));
7386
7387
0
    errno = xerrno;
7388
0
    return -1;
7389
0
  }
7390
7391
# if defined(HAVE_STATFS_F_FSTYPENAME)
7392
  pr_trace_msg(trace_channel, 12,
7393
    "path '%s' resides on a filesystem of type '%s'", path, fs.f_fstypename);
7394
  if (strcasecmp(fs.f_fstypename, "nfs") == 0) {
7395
    res = TRUE;
7396
  }
7397
# elif defined(HAVE_STATFS_F_TYPE)
7398
  /* Probably a Linux system. */
7399
0
  if (fs.f_type == NFS_SUPER_MAGIC) {
7400
0
    pr_trace_msg(trace_channel, 12,
7401
0
      "path '%s' resides on an NFS_SUPER_MAGIC filesystem (type 0x%08x)", path,
7402
0
      (int) fs.f_type);
7403
0
    res = TRUE;
7404
7405
0
  } else {
7406
0
    pr_trace_msg(trace_channel, 12,
7407
0
      "path '%s' resides on a filesystem of type 0x%08x (not NFS_SUPER_MAGIC)",
7408
0
      path, (int) fs.f_type);
7409
0
  }
7410
0
# endif
7411
7412
0
  return res;
7413
7414
#else
7415
  errno = ENOSYS;
7416
  return -1;
7417
#endif /* No HAVE_STATFS_F_FSTYPENAME or HAVE_STATFS_F_TYPE */
7418
0
}
7419
7420
0
int pr_fsio_puts(const char *buf, pr_fh_t *fh) {
7421
0
  if (fh == NULL ||
7422
0
      buf == NULL) {
7423
0
    errno = EINVAL;
7424
0
    return -1;
7425
0
  }
7426
7427
0
  return pr_fsio_write(fh, buf, strlen(buf));
7428
0
}
7429
7430
0
int pr_fsio_set_block(pr_fh_t *fh) {
7431
0
  int flags, res;
7432
7433
0
  if (fh == NULL) {
7434
0
    errno = EINVAL;
7435
0
    return -1;
7436
0
  }
7437
7438
0
  flags = fcntl(fh->fh_fd, F_GETFL);
7439
0
  if (flags < 0) {
7440
0
    return -1;
7441
0
  }
7442
7443
0
  res = fcntl(fh->fh_fd, F_SETFL, flags & (U32BITS ^ O_NONBLOCK));
7444
0
  return res;
7445
0
}
7446
7447
0
void pr_resolve_fs_map(void) {
7448
0
  register unsigned int i = 0;
7449
7450
0
  if (fs_map == NULL) {
7451
0
    return;
7452
0
  }
7453
7454
0
  for (i = 0; i < fs_map->nelts; i++) {
7455
0
    char *newpath = NULL;
7456
0
    int add_slash = FALSE;
7457
0
    pr_fs_t *fsi;
7458
7459
0
    pr_signals_handle();
7460
0
    fsi = ((pr_fs_t **) fs_map->elts)[i];
7461
7462
    /* Skip if this fs is the root fs. */
7463
0
    if (fsi == root_fs) {
7464
0
      continue;
7465
0
    }
7466
7467
    /* Note that dir_realpath() does _not_ handle "../blah" paths
7468
     * well, so...at least for now, hope that such paths are screened
7469
     * by the code adding such paths into the fs_map.  Check for
7470
     * a trailing slash in the unadjusted path, so that I know if I need
7471
     * to re-add that slash to the adjusted path -- these trailing slashes
7472
     * are important!
7473
     */
7474
0
    if ((strcmp(fsi->fs_path, "/") != 0 &&
7475
0
        (fsi->fs_path)[strlen(fsi->fs_path) - 1] == '/')) {
7476
0
      add_slash = TRUE;
7477
0
    }
7478
7479
0
    newpath = dir_realpath(fsi->fs_pool, fsi->fs_path);
7480
0
    if (newpath != NULL) {
7481
7482
0
      if (add_slash == TRUE) {
7483
0
        newpath = pstrcat(fsi->fs_pool, newpath, "/", NULL);
7484
0
      }
7485
7486
      /* Note that this does cause a slightly larger memory allocation from
7487
       * the pr_fs_t's pool, as the original path value was also allocated
7488
       * from that pool, and that original pointer is being overwritten.
7489
       * However, as this function is only called once, and that pool
7490
       * is freed later, I think this may be acceptable.
7491
       */
7492
0
      fsi->fs_path = newpath;
7493
0
    }
7494
0
  }
7495
7496
  /* Resort the map */
7497
0
  qsort(fs_map->elts, fs_map->nelts, sizeof(pr_fs_t *), fs_cmp);
7498
0
}
7499
7500
0
int init_fs(void) {
7501
0
  char cwdbuf[PR_TUNABLE_PATH_MAX + 1] = {'\0'};
7502
7503
  /* Establish the default pr_fs_t that will handle any path */
7504
0
  root_fs = pr_create_fs(permanent_pool, "system");
7505
0
  if (root_fs == NULL) {
7506
7507
    /* Do not insert this fs into the FS map.  This will allow other
7508
     * modules to insert filesystems at "/", if they want.
7509
     */
7510
0
    pr_log_pri(PR_LOG_WARNING, "error: unable to initialize default FS");
7511
0
    exit(1);
7512
0
  }
7513
7514
0
  root_fs->fs_path = pstrdup(root_fs->fs_pool, "/");
7515
7516
  /* Set the root FSIO handlers. */
7517
0
  root_fs->stat = sys_stat;
7518
0
  root_fs->fstat = sys_fstat;
7519
0
  root_fs->lstat = sys_lstat;
7520
0
  root_fs->rename = sys_rename;
7521
0
  root_fs->unlink = sys_unlink;
7522
0
  root_fs->open = sys_open;
7523
0
  root_fs->close = sys_close;
7524
0
  root_fs->pread = sys_pread;
7525
0
  root_fs->read = sys_read;
7526
0
  root_fs->pwrite = sys_pwrite;
7527
0
  root_fs->write = sys_write;
7528
0
  root_fs->lseek = sys_lseek;
7529
0
  root_fs->link = sys_link;
7530
0
  root_fs->readlink = sys_readlink;
7531
0
  root_fs->symlink = sys_symlink;
7532
0
  root_fs->ftruncate = sys_ftruncate;
7533
0
  root_fs->truncate = sys_truncate;
7534
0
  root_fs->chmod = sys_chmod;
7535
0
  root_fs->fchmod = sys_fchmod;
7536
0
  root_fs->chown = sys_chown;
7537
0
  root_fs->fchown = sys_fchown;
7538
0
  root_fs->lchown = sys_lchown;
7539
0
  root_fs->access = sys_access;
7540
0
  root_fs->faccess = sys_faccess;
7541
0
  root_fs->utimes = sys_utimes;
7542
0
  root_fs->futimes = sys_futimes;
7543
0
  root_fs->fsync = sys_fsync;
7544
0
  root_fs->realpath = sys_realpath;
7545
7546
0
  root_fs->getxattr = sys_getxattr;
7547
0
  root_fs->lgetxattr = sys_lgetxattr;
7548
0
  root_fs->fgetxattr = sys_fgetxattr;
7549
0
  root_fs->listxattr = sys_listxattr;
7550
0
  root_fs->llistxattr = sys_llistxattr;
7551
0
  root_fs->flistxattr = sys_flistxattr;
7552
0
  root_fs->removexattr = sys_removexattr;
7553
0
  root_fs->lremovexattr = sys_lremovexattr;
7554
0
  root_fs->fremovexattr = sys_fremovexattr;
7555
0
  root_fs->setxattr = sys_setxattr;
7556
0
  root_fs->lsetxattr = sys_lsetxattr;
7557
0
  root_fs->fsetxattr = sys_fsetxattr;
7558
7559
0
  root_fs->chdir = sys_chdir;
7560
0
  root_fs->chroot = sys_chroot;
7561
0
  root_fs->opendir = sys_opendir;
7562
0
  root_fs->closedir = sys_closedir;
7563
0
  root_fs->readdir = sys_readdir;
7564
0
  root_fs->mkdir = sys_mkdir;
7565
0
  root_fs->rmdir = sys_rmdir;
7566
7567
0
  if (getcwd(cwdbuf, sizeof(cwdbuf)-1)) {
7568
0
    cwdbuf[sizeof(cwdbuf)-1] = '\0';
7569
0
    pr_fs_setcwd(cwdbuf);
7570
7571
0
  } else {
7572
0
    pr_fsio_chdir("/", FALSE);
7573
0
    pr_fs_setcwd("/");
7574
0
  }
7575
7576
  /* Prepare the stat cache as well. */
7577
0
  statcache_pool = make_sub_pool(permanent_pool);
7578
0
  pr_pool_tag(statcache_pool, "FS Statcache Pool");
7579
0
  stat_statcache_tab = pr_table_alloc(statcache_pool, 0);
7580
0
  stat_statcache_set = xaset_create(statcache_pool, NULL);
7581
0
  lstat_statcache_tab = pr_table_alloc(statcache_pool, 0);
7582
0
  lstat_statcache_set = xaset_create(statcache_pool, NULL);
7583
7584
0
  return 0;
7585
0
}
7586
7587
#ifdef PR_USE_DEVEL
7588
7589
static const char *get_fs_hooks_str(pool *p, pr_fs_t *fs) {
7590
  char *hooks = "";
7591
7592
  if (fs->stat) {
7593
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "stat(2)", NULL);
7594
  }
7595
7596
  if (fs->lstat) {
7597
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lstat(2)", NULL);
7598
  }
7599
7600
  if (fs->fstat) {
7601
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fstat(2)", NULL);
7602
  }
7603
7604
  if (fs->rename) {
7605
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "rename(2)", NULL);
7606
  }
7607
7608
  if (fs->link) {
7609
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "link(2)", NULL);
7610
  }
7611
7612
  if (fs->unlink) {
7613
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "unlink(2)", NULL);
7614
  }
7615
7616
  if (fs->open) {
7617
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "open(2)", NULL);
7618
  }
7619
7620
  if (fs->close) {
7621
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "close(2)", NULL);
7622
  }
7623
7624
  if (fs->read) {
7625
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "read(2)", NULL);
7626
  }
7627
7628
  if (fs->lseek) {
7629
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lseek(2)", NULL);
7630
  }
7631
7632
  if (fs->readlink) {
7633
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "readlink(2)", NULL);
7634
  }
7635
7636
  if (fs->symlink) {
7637
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "symlink(2)", NULL);
7638
  }
7639
7640
  if (fs->ftruncate) {
7641
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "ftruncate(2)", NULL);
7642
  }
7643
7644
  if (fs->truncate) {
7645
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "truncate(2)", NULL);
7646
  }
7647
7648
  if (fs->chmod) {
7649
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chmod(2)", NULL);
7650
  }
7651
7652
  if (fs->chown) {
7653
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chown(2)", NULL);
7654
  }
7655
7656
  if (fs->fchown) {
7657
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fchown(2)", NULL);
7658
  }
7659
7660
  if (fs->lchown) {
7661
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "lchown(2)", NULL);
7662
  }
7663
7664
  if (fs->access) {
7665
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "access(2)", NULL);
7666
  }
7667
7668
  if (fs->faccess) {
7669
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "faccess(2)", NULL);
7670
  }
7671
7672
  if (fs->utimes) {
7673
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "utimes(2)", NULL);
7674
  }
7675
7676
  if (fs->futimes) {
7677
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "futimes(2)", NULL);
7678
  }
7679
7680
  if (fs->fsync) {
7681
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "fsync(2)", NULL);
7682
  }
7683
7684
  if (fs->chdir) {
7685
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chdir(2)", NULL);
7686
  }
7687
7688
  if (fs->chroot) {
7689
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "chroot(2)", NULL);
7690
  }
7691
7692
  if (fs->opendir) {
7693
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "opendir(3)", NULL);
7694
  }
7695
7696
  if (fs->closedir) {
7697
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "closedir(3)", NULL);
7698
  }
7699
7700
  if (fs->readdir) {
7701
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "readdir(3)", NULL);
7702
  }
7703
7704
  if (fs->mkdir) {
7705
    hooks = pstrcat(p, hooks, *hooks ? ", " : "", "mkdir(2)", NULL);
7706
  }
7707
7708
  if (!*hooks) {
7709
    return pstrdup(p, "(none)");
7710
  }
7711
7712
  return hooks;
7713
}
7714
7715
static void get_fs_info(pool *p, int depth, pr_fs_t *fs,
7716
    void (*dumpf)(const char *, ...)) {
7717
7718
  dumpf("FS#%u: '%s', mounted at '%s', implementing the following hooks:",
7719
    depth, fs->fs_name, fs->fs_path);
7720
  dumpf("FS#%u:    %s", depth, get_fs_hooks_str(p, fs));
7721
}
7722
7723
static void fs_printf(const char *fmt, ...) {
7724
  char buf[PR_TUNABLE_BUFFER_SIZE+1];
7725
  va_list msg;
7726
7727
  memset(buf, '\0', sizeof(buf));
7728
  va_start(msg, fmt);
7729
  pr_vsnprintf(buf, sizeof(buf)-1, fmt, msg);
7730
  va_end(msg);
7731
7732
  buf[sizeof(buf)-1] = '\0';
7733
  pr_trace_msg(trace_channel, 19, "%s", buf);
7734
}
7735
7736
void pr_fs_dump(void (*dumpf)(const char *, ...)) {
7737
  pool *p;
7738
7739
  if (dumpf == NULL) {
7740
    dumpf = fs_printf;
7741
  }
7742
7743
  dumpf("FS#0: 'system' mounted at '/', implementing the following hooks:");
7744
  dumpf("FS#0:    (all)");
7745
7746
  if (!fs_map ||
7747
      fs_map->nelts == 0) {
7748
    return;
7749
  }
7750
7751
  p = make_sub_pool(permanent_pool);
7752
7753
  if (fs_map->nelts > 0) {
7754
    pr_fs_t **fs_objs = (pr_fs_t **) fs_map->elts;
7755
    register unsigned int i;
7756
7757
    for (i = 0; i < fs_map->nelts; i++) {
7758
      pr_fs_t *fsi = fs_objs[i];
7759
7760
      for (; fsi->fs_next; fsi = fsi->fs_next) {
7761
        get_fs_info(p, i+1, fsi, dumpf);
7762
      }
7763
    }
7764
  }
7765
7766
  destroy_pool(p);
7767
}
7768
#endif /* PR_USE_DEVEL */