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