Coverage Report

Created: 2024-02-11 06:31

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