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
</