fs.c
上传用户:pycemail
上传日期:2007-01-04
资源大小:329k
文件大小:29k
源码类别:

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD - FTP server daemon
  3.  * Copyright (c) 1997, 1998 Public Flood Software
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  18.  */
  19. /* ProFTPD virtual/modular file-system support
  20.  * $Id: fs.c,v 1.6 1999/09/18 18:26:38 macgyver Exp $
  21.  */
  22. #include "conf.h"
  23. typedef struct opendir_struct opendir_t;
  24. struct opendir_struct {
  25.   opendir_t *next,*prev;
  26.   DIR *dir;
  27.   fsdir_t *fsdir;
  28. };
  29. static fsdir_t *fs = NULL;
  30. static fsmatch_t *fs_match = NULL;
  31. static fsdir_t *fs_cwd = NULL,*fs_std = NULL;
  32. static xaset_t *opendir_list;
  33. static void *fs_cache_dir = NULL;
  34. static fsdir_t *fs_cache_fsdir = NULL;
  35. /* virtual working directory */
  36. static char vwd[MAXPATHLEN] = "/";
  37. static char cwd[MAXPATHLEN] = "/";
  38. /* the following static functions are simply wrappers for libc
  39.  * (the fs_std)
  40.  */
  41. static int std_stat(fsdir_t *f, const char *path, struct stat *sbuf)
  42. { return stat(path,sbuf); }
  43. static int std_lstat(fsdir_t *f, const char *path, struct stat *sbuf)
  44. { return lstat(path,sbuf); }
  45. static int std_rename(fsdir_t *f, const char *ren_from, const char *ren_to)
  46. { return rename(ren_from,ren_to); }
  47. static int std_unlink(fsdir_t *f, const char *path)
  48. { return unlink(path); }
  49. static int std_open(fsdir_t *f, const char *path, int access)
  50. { return open(path,access,0666); }
  51. static int std_creat(fsdir_t *f,const char *path, mode_t mode)
  52. { return creat(path,mode); }
  53. static int std_close(fsdir_t *f, int fd)
  54. { return close(fd); }
  55. static int std_read(fsdir_t *f, int fd, char *buf, size_t size)
  56. { return read(fd,buf,size); }
  57. static int std_write(fsdir_t *f, int fd, const char *buf, size_t size)
  58. { return write(fd,buf,size); }
  59. static off_t std_lseek(fsdir_t *f, int fd, off_t offset, int whence)
  60. { return lseek(fd,offset,whence); }
  61. static int std_link(fsdir_t *f, const char *path1, const char *path2)
  62. { return link(path1,path2); }
  63. static int std_symlink(fsdir_t *f, const char *path1, const char *path2)
  64. { return symlink(path1,path2); }
  65. static int std_readlink(fsdir_t *f, const char *path, char *buf, size_t max)
  66. { return readlink(path,buf,max); }
  67. static int std_chmod(fsdir_t *f, const char *path, mode_t mode)
  68. { return chmod(path,mode); }
  69. static int std_chown(fsdir_t *f, const char *path, uid_t uid, gid_t gid)
  70. { return chown(path,uid,gid); }
  71. static int std_chdir(fsdir_t *f, const char *path)
  72. { return chdir(path); }
  73. static void *std_opendir(fsdir_t *f, const char *path)
  74. { return opendir(path); }
  75. static int std_closedir(fsdir_t *f, void *dir)
  76. { return closedir((DIR*)dir); }
  77. static struct dirent *std_readdir(fsdir_t *f, void *dir)
  78. { return readdir((DIR*)dir); }
  79. fsmatch_t *fs_register_match(char *name, int opmask)
  80. {
  81.   fsmatch_t *newfs;
  82.   if(!name)
  83.     return NULL;
  84.   newfs = calloc(1,sizeof(fsmatch_t));
  85.   if(!newfs)
  86.     return NULL;
  87.   newfs->name = strdup(name);
  88.   newfs->opmask = opmask;
  89.   newfs->next = fs_match;
  90.   fs_match = newfs;
  91.   return newfs;
  92. }
  93. fsdir_t *fs_register(char *name)
  94. {
  95.   fsdir_t *parent = NULL,*f,*newfs;
  96.   int best = 0,i;
  97.   newfs = calloc(1,sizeof(fsdir_t));
  98.   if(!newfs)
  99.     return NULL;
  100.   if(name)
  101.     newfs->name = strdup(name);
  102.   newfs->stat = std_stat;
  103.   newfs->lstat = std_lstat;
  104.   newfs->rename = std_rename;
  105.   newfs->unlink = std_unlink;
  106.   newfs->open = std_open;
  107.   newfs->creat = std_creat;
  108.   newfs->close = std_close;
  109.   newfs->read = std_read;
  110.   newfs->write = std_write;
  111.   newfs->lseek = std_lseek;
  112.   newfs->link = std_link;
  113.   newfs->symlink = std_symlink;
  114.   newfs->chmod = std_chmod;
  115.   newfs->chown = std_chown;
  116.   newfs->chdir = std_chdir;
  117.   newfs->readlink = std_readlink;
  118.   newfs->opendir = std_opendir;
  119.   newfs->closedir = std_closedir;
  120.   newfs->readdir = std_readdir;
  121.   /* find proper parent */
  122.   if(newfs->name) {
  123.     for(f = fs; f; f=f->next) {
  124.       if(f->name) {
  125.         i = strlen(f->name);
  126.         if(!strncmp(f->name,newfs->name,i) && i > best) {
  127.           best = i; 
  128.           parent = f;
  129.         }
  130.       }
  131.     }
  132.     if(parent)
  133.       newfs->parent = parent;
  134.     else
  135.       newfs->parent = fs_std;
  136.   }
  137.   newfs->next = fs;
  138.   fs = newfs;
  139.   return newfs;
  140. }
  141. void fs_unregister(fsdir_t *fsd)
  142. {
  143.   /* not yet done */
  144. }
  145. static fsdir_t *_scan_fsdir(const char *path)
  146. {
  147.   fsdir_t *f,*found = NULL;
  148.   register int i,best = 0;
  149.   for(f = fs; f; f=f->next) {
  150.     if(f->name) {
  151.       i = strlen(f->name);
  152.       if(!strncmp(f->name,path,i) && i > best) {
  153.         best = i;
  154.         found = f;
  155.       }
  156.     }
  157.   }
  158.   return found;
  159. }
  160. static fsmatch_t *_scan_fsmatch(const char *path, int op)
  161. {
  162.   fsmatch_t *fm;
  163.   for(fm = fs_match; fm; fm=fm->next) {
  164.     if((fm->opmask & op) && fnmatch(fm->name,path,0) == 0)
  165.       return fm;
  166.   }
  167.   return NULL;
  168. }
  169. /* fs_lookup_dir() is called when we want to perform some sort of
  170.  * directory operation on a directory or file.  A "closest" match
  171.  * algorithm is used.  If the lookup fails or is not "close enough"
  172.  * (i.e. the final target does not exactly match an existing fsdir_t,
  173.  * we can for matchable targets and call dir_hit, then rescan
  174.  * the fsdir_t list.  The rescan is performed in case any modules
  175.  * registered fs handlers during the hit.
  176.  */
  177. fsdir_t *fs_lookup_dir(const char *path,int op)
  178. {
  179.   char buf[MAXPATHLEN];
  180.   char tmpbuf[MAXPATHLEN];
  181.   fsdir_t *f;
  182.   fsmatch_t *fm = NULL;
  183.   int exact = 0;
  184.   sstrncpy(buf,path,MAXPATHLEN);
  185.   if(buf[0] != '/')
  186.     fs_dircat(tmpbuf,sizeof(tmpbuf),cwd,buf);
  187.   else
  188.     sstrncpy(tmpbuf,buf,sizeof(tmpbuf));
  189.   
  190.   f = _scan_fsdir(tmpbuf);
  191.   /* we found a match, now see if it's close enough */
  192.   if(f) {
  193.     if(!strcmp(tmpbuf,f->name))
  194.       exact = 1;
  195.   }
  196.   if(!f || !exact) {
  197.     /* look for a fsmatch */
  198.     fm = _scan_fsmatch(tmpbuf,op);
  199.     if(op & FSOP_FILEMASK) {
  200.       if(fm && fm->file_hit((f ? f : fs_std),tmpbuf,op) > 0)
  201.         /* now rescan */
  202.         f = _scan_fsdir(tmpbuf);
  203.     } else {
  204.       if(fm && fm->dir_hit((f ? f : fs_std),tmpbuf,op) > 0)
  205.         f = _scan_fsdir(tmpbuf);
  206.     }
  207.   }
  208.   return (f ? f : fs_std);
  209. }
  210. /* fs_lookup_file() performs the same function as fs_lookup_dir, however
  211.  * because we are performing a file lookup, the target is the subdirectory
  212.  * _containing_ the actual target.  A basic optimization is used here,
  213.  * if the path contains no '/' characters, fs_cwd is returned.
  214.  */
  215. fsdir_t *fs_lookup_file(const char *path, char **deref, int op)
  216. {
  217.   if(!strchr(path,'/')) {
  218.     fsmatch_t *fm;
  219.     fm = _scan_fsmatch(path,op);
  220.     if(!fm || fm->file_hit(fs_cwd,path,op) <= 0) {
  221.       struct stat sbuf;
  222.       if(fs_cwd->lstat(fs_cwd,path,&sbuf) == -1 || !S_ISLNK(sbuf.st_mode))
  223.         return fs_cwd;
  224.     } else {
  225.       char linkbuf[MAXPATHLEN];
  226.       int i;
  227.       if((i = fs_cwd->readlink(fs_cwd,path, &linkbuf[2],
  228.        MAXPATHLEN - 3)) != -1) {
  229.         linkbuf[i] = '';
  230.         if(!strchr(linkbuf, '/')) {
  231.           if(i + 3 > MAXPATHLEN)
  232.             i = MAXPATHLEN - 3;
  233.           memmove(&linkbuf[2], linkbuf, i + 1);
  234.   
  235.           linkbuf[i+2] = '';
  236.           linkbuf[0] = '.';
  237.           linkbuf[1] = '/';
  238.           return fs_lookup_file_canon(linkbuf,deref,op);
  239.         }
  240.       }
  241.       
  242.       return fs_cwd;
  243.     }
  244.   }
  245. #if 0
  246.   target = rindex(workpath,'/');
  247.   if(target == workpath) {
  248.     if(*(target+1))
  249.       *target = '';
  250.   } else if(target)
  251.     *target = '';
  252. #endif
  253.   return fs_lookup_dir(path,op);
  254. }
  255. fsdir_t *fs_lookup_file_canon(const char *path, char **deref, int op)
  256. {
  257.   static char workpath[MAXPATHLEN];
  258.   if(fs_resolve_partial(path, workpath, MAXPATHLEN, FSOP_OPEN) == -1) {
  259.     if(*path == '/' || *path == '~')
  260.       fs_interpolate(path, workpath, MAXPATHLEN);
  261.     else
  262.       fs_dircat(workpath, sizeof(workpath), cwd, path);
  263.   }
  264.   if(deref)
  265.     *deref = workpath;
  266.   return fs_lookup_file(workpath, deref, op);
  267. }
  268. void fs_setcwd(const char *dir)
  269. {
  270.   fs_resolve_path(dir,cwd,MAXPATHLEN,FSOP_CHDIR);
  271.   sstrncpy(vwd,dir,MAXPATHLEN);
  272.   fs_cwd = fs_lookup_dir(cwd,FSOP_CHDIR);
  273. }
  274. const char *fs_getcwd()
  275. { return cwd; }
  276. const char *fs_getvwd()
  277. { return vwd; }
  278. void fs_dircat(char *buf, int len, const char *dir1, const char *dir2)
  279. {
  280.   /* make temporary copies so that memory areas can overlap */
  281.   char *_dir1, *_dir2;
  282.   int i;
  283.   /* This is an experimental test to see if we've got reasonable
  284.    * directories to concatenate.  If we don't, then we default to
  285.    * the root directory.
  286.    */
  287.   if((strlen(dir1) + strlen(dir2) + 1) > MAXPATHLEN) {
  288.     sstrncpy(buf, "/", len);
  289.     return;
  290.   }
  291.   
  292.   _dir1 = strdup(dir1);
  293.   _dir2 = strdup(dir2);
  294.   
  295.   i = strlen(_dir1) - 1;
  296.   
  297.   if(*_dir2 == '/') {
  298.     sstrncpy(buf, _dir2, len);
  299.     free(_dir1);
  300.     free(_dir2);
  301.     return;
  302.   }
  303.   
  304.   sstrncpy(buf, _dir1, len);
  305.   if(i && *(_dir1 + i) != '/')
  306.     sstrcat(buf, "/", MAXPATHLEN);
  307.   sstrcat(buf, _dir2, MAXPATHLEN);
  308.   
  309.   if(!*buf) {
  310.    *buf++ = '/';
  311.    *buf = '';
  312.   }
  313.   
  314.   free(_dir1);
  315.   free(_dir2);
  316. }
  317. int fs_interpolate(const char *path, char *buf, int maxlen)
  318. {
  319.   pool *p;
  320.   struct passwd *pw;
  321.   char *user,*tmp;
  322.   char workpath[MAXPATHLEN];
  323.   int i;
  324.   if(!path)
  325.     return -1;
  326.   sstrncpy(workpath,path,MAXPATHLEN);
  327.   if(workpath[0] == '~') {
  328.     user = &workpath[1];
  329.     tmp = index(user,'/');
  330.     if(tmp)
  331.       *tmp++ = '';
  332.     if(!*user)
  333.       user = session.user;
  334.     p = make_sub_pool(permanent_pool);
  335.     pw = auth_getpwnam(p,user);
  336.     destroy_pool(p);
  337.     if(!pw) {
  338.       errno = ENOENT;
  339.       return -1;
  340.     }
  341.     sstrncpy(buf,pw->pw_dir,maxlen);
  342.     i = strlen(buf);
  343.     if(tmp && i < maxlen && buf[i-1] != '/')
  344.       buf[i++] = '/';
  345.     if(tmp)
  346.       sstrncpy(&buf[i],tmp,maxlen - i);
  347.   } else
  348.     sstrncpy(buf,workpath,maxlen);
  349.   return 0;
  350. }
  351. int fs_resolve_partial(const char *path, char *buf, int maxlen, int op)
  352. {
  353.   char curpath[MAXPATHLEN] = "",
  354.        workpath[MAXPATHLEN] = "",
  355.        namebuf[MAXPATHLEN],
  356.        linkpath[MAXPATHLEN],
  357.        *where,*ptr,*last;
  358.   fsdir_t *f;
  359.   int len,fini = 1,link_cnt = 0;
  360.   ino_t last_inode = 0;
  361.   struct stat sbuf;
  362.   if(!path)
  363.     return -1;
  364.   if(*path != '/') {
  365.     if(*path == '~')
  366.       fs_interpolate(path,curpath,sizeof(curpath));
  367.     else {
  368.       sstrncpy(curpath,path,sizeof(curpath));
  369.       sstrncpy(workpath,cwd,sizeof(workpath));
  370.     }
  371.   } else
  372.     sstrncpy(curpath,path,sizeof(curpath));
  373.   while(fini--) {
  374.     where = curpath;
  375.     while(*where != '') {
  376.       if(!strcmp(where, ".")) {
  377.         where++;
  378.         continue;
  379.       }
  380.       
  381.       /* handle ".." */
  382.       if(!strcmp(where,"..")) {
  383.         where += 2;
  384.         ptr = last = workpath;
  385.         while(*ptr) {
  386.           if(*ptr == '/')
  387.             last = ptr;
  388.           ptr++;
  389.         }
  390.         *last = '';
  391.         continue;
  392.       }
  393.       
  394.       /* handle "./" */
  395.       if(!strncmp(where, "./", 2)) {
  396.         where += 2;
  397.         continue;
  398.       }
  399.       
  400.       /* handle "../" */
  401.       if(!strncmp(where, "../", 3)) {
  402.         where += 3;
  403.         ptr = last = workpath;
  404.         while(*ptr) {
  405.           if(*ptr == '/')
  406.             last = ptr;
  407.           ptr++;
  408.         }
  409.         *last = '';
  410.         continue;
  411.       }
  412.       
  413.       ptr = strchr(where, '/');
  414.       if(!ptr)
  415.         ptr = where + strlen(where) - 1;
  416.       else
  417.         *ptr = '';
  418.       sstrncpy(namebuf, workpath, sizeof(namebuf));
  419.       if(*namebuf) {
  420.         for(last = namebuf; *last; last++) ;
  421.         if(*--last != '/')
  422.           sstrcat(namebuf, "/", MAXPATHLEN);
  423.       } else
  424.         sstrcat(namebuf, "/", MAXPATHLEN);
  425.       
  426.       sstrcat(namebuf, where, MAXPATHLEN);
  427.       
  428.       where = ++ptr;
  429.       f = fs_lookup_dir(namebuf,op);
  430.       
  431.       if(f->lstat(f, namebuf, &sbuf) == -1) {
  432.         errno = ENOENT;
  433.         return -1;
  434.       }
  435.       
  436.       if(S_ISLNK(sbuf.st_mode)) {
  437.         /* Detect an obvious recursive symlink */
  438.         if(sbuf.st_ino && (ino_t)sbuf.st_ino == last_inode) {
  439.           errno = ENOENT;
  440.           return -1;
  441.         }
  442.         last_inode = (ino_t)sbuf.st_ino;
  443.         if(++link_cnt > 32) {
  444.           errno = ELOOP;
  445.           return -1;
  446.         }
  447.         if((len = f->readlink(f,namebuf,linkpath,MAXPATHLEN)) <= 0) {
  448.           errno = ENOENT;
  449.           return -1;
  450.         }
  451.         *(linkpath+len) = '';
  452.         if(*linkpath == '/')
  453.           *workpath = '';
  454.         if(*linkpath == '~') {
  455.           char tmpbuf[MAXPATHLEN];
  456.           *workpath = '';
  457.           sstrncpy(tmpbuf,linkpath,sizeof(tmpbuf));
  458.           fs_interpolate(tmpbuf,linkpath,sizeof(linkpath));
  459.         }
  460.         if(*where) {
  461.           sstrcat(linkpath,"/",MAXPATHLEN);
  462.           sstrcat(linkpath,where,MAXPATHLEN);
  463.         }
  464.         sstrncpy(curpath,linkpath,sizeof(curpath));
  465.         fini++;
  466.         break; /* continue main loop */
  467.       }
  468.       if(S_ISDIR(sbuf.st_mode)) {
  469.         sstrncpy(workpath, namebuf, sizeof(workpath));
  470.         continue;
  471.       }
  472.       if(*where) {
  473.         errno = ENOENT;
  474.         return -1;               /* path/notadir/morepath */
  475.       } else
  476.         sstrncpy(workpath, namebuf, sizeof(workpath));
  477.     }
  478.   }
  479.   if(!workpath[0])
  480.     sstrncpy(workpath, "/", sizeof(workpath));
  481.   sstrncpy(buf, workpath, maxlen);
  482.   return 0;
  483. }
  484.   
  485. int fs_resolve_path(const char *path, char *buf, int maxlen, int op)
  486. {
  487.   char curpath[MAXPATHLEN],
  488.        workpath[MAXPATHLEN],
  489.        namebuf[MAXPATHLEN],
  490.        linkpath[MAXPATHLEN],
  491.        *where,*ptr,*last;
  492.   fsdir_t *f;
  493.   int len,fini = 1,link_cnt = 0;
  494.   ino_t last_inode = 0;
  495.   struct stat sbuf;
  496.   if(!path)
  497.     return -1;
  498.   if(fs_interpolate(path,curpath,MAXPATHLEN) == -1)
  499.     sstrncpy(curpath,path,MAXPATHLEN);
  500.   if(curpath[0] != '/')
  501.     sstrncpy(workpath,cwd,MAXPATHLEN);
  502.   else
  503.     workpath[0] = '';
  504.   while(fini--) {
  505.     where = curpath;
  506.     while(*where != '') {
  507.       if(!strcmp(where,".")) {
  508.         where++;
  509.         continue;
  510.       }
  511.       /* handle "./" */
  512.       if(!strncmp(where,"./",2)) {
  513.         where+=2;
  514.         continue;
  515.       }
  516.       /* handle "../" */
  517.       if(!strncmp(where,"../",3)) {
  518.         where+=3;
  519.         ptr = last = workpath;
  520.         while(*ptr) {
  521.           if(*ptr == '/')
  522.             last = ptr;
  523.           ptr++;
  524.         }
  525.         *last = '';
  526.         continue;
  527.       }
  528.       ptr = strchr(where,'/');
  529.       if(!ptr)
  530.         ptr = where + strlen(where) - 1;
  531.       else
  532.         *ptr = '';
  533.       sstrncpy(namebuf,workpath,sizeof(namebuf));
  534.       if(*namebuf) {
  535.         for(last = namebuf; *last; last++) ;
  536.         if(*--last != '/')
  537.           sstrcat(namebuf,"/",MAXPATHLEN);
  538.       } else
  539.         sstrcat(namebuf,"/",MAXPATHLEN);
  540.       sstrcat(namebuf,where,MAXPATHLEN);
  541.       where = ++ptr;
  542.       f = fs_lookup_dir(namebuf,op);
  543.       if(f->lstat(f,namebuf,&sbuf) == -1) {
  544.         errno = ENOENT;
  545.         return -1;
  546.       }
  547.     
  548.       if(S_ISLNK(sbuf.st_mode)) {
  549.         /* Detect an obvious recursive symlink */
  550.         if(sbuf.st_ino && (ino_t)sbuf.st_ino == last_inode) {
  551.           errno = ENOENT;
  552.           return -1;
  553.         }
  554.         last_inode = (ino_t)sbuf.st_ino;
  555.         if(++link_cnt > 32) {
  556.           errno = ELOOP;
  557.           return -1;
  558.         }
  559.         if((len = f->readlink(f,namebuf,linkpath,MAXPATHLEN)) <= 0) {
  560.           errno = ENOENT;
  561.           return -1;
  562.         }
  563.         *(linkpath+len) = '';
  564.         if(*linkpath == '/')
  565.           *workpath = '';
  566.         if(*linkpath == '~') {
  567.           char tmpbuf[MAXPATHLEN];
  568.           *workpath = '';
  569.           sstrncpy(tmpbuf,linkpath,sizeof(tmpbuf));
  570.           fs_interpolate(tmpbuf,linkpath,sizeof(linkpath));
  571.         }
  572.         if(*where) {
  573.           sstrcat(linkpath,"/",MAXPATHLEN);
  574.           sstrcat(linkpath,where,MAXPATHLEN);
  575.         }
  576.         sstrncpy(curpath,linkpath,sizeof(curpath));
  577.         fini++;
  578.         break; /* continue main loop */
  579.       }
  580.       if(S_ISDIR(sbuf.st_mode)) {
  581.         sstrncpy(workpath,namebuf,sizeof(workpath));
  582.         continue;
  583.       }
  584.       if(*where) {
  585.         errno = ENOENT;
  586.         return -1;               /* path/notadir/morepath */
  587.       } else
  588.         sstrncpy(workpath,namebuf,sizeof(workpath));
  589.     }
  590.   }
  591.   if(!workpath[0])
  592.     sstrncpy(workpath,"/",sizeof(workpath));
  593.   sstrncpy(buf,workpath,maxlen);
  594.   return 0;
  595. }
  596. void fs_clean_path(const char *path, char *buf, int maxlen)
  597. {
  598.   char workpath[MAXPATHLEN] = "";
  599.   char curpath[MAXPATHLEN];
  600.   char namebuf[MAXPATHLEN];
  601.   char *where,*ptr,*last;
  602.   int fini = 1;
  603.   if(!path)
  604.     return;
  605.   sstrncpy(curpath,path,MAXPATHLEN);
  606.   
  607.   /* main loop */
  608.   while(fini--) {
  609.     where = curpath;
  610.     while(*where != '') {
  611.       if(!strcmp(where,".")) {
  612.         where++;
  613.         continue;
  614.       }
  615.       /* handle "./" */
  616.       if(!strncmp(where,"./",2)) {
  617.         where += 2;
  618.         continue;
  619.       }
  620.       /* handle ".." */
  621.       if(!strcmp(where,"..")) {
  622.         where+=2;
  623.         ptr = last = workpath;
  624.         while(*ptr) {
  625.           if(*ptr == '/')
  626.             last = ptr;
  627.           ptr++;
  628.         }
  629.         *last = '';
  630.         continue;
  631.       }
  632.       /* handle "../" */
  633.       if(!strncmp(where,"../",3)) {
  634.         where += 3;
  635.         ptr = last = workpath;
  636.         while(*ptr) {
  637.           if(*ptr == '/')
  638.             last = ptr;
  639.           ptr++;
  640.         }
  641.         *last = '';
  642.         continue;
  643.       }
  644.       ptr = strchr(where,'/');
  645.       if(!ptr)
  646.         ptr = where + strlen(where) - 1;
  647.       else
  648.         *ptr = '';
  649.       sstrncpy(namebuf,workpath,MAXPATHLEN);
  650.       
  651.       if(*namebuf) {
  652.         for(last = namebuf; *last; last++) ;
  653.         if(*--last != '/')
  654.           sstrcat(namebuf,"/",MAXPATHLEN);
  655.       } else
  656.         sstrcat(namebuf,"/",MAXPATHLEN);
  657.       sstrcat(namebuf,where,MAXPATHLEN);
  658.       namebuf[MAXPATHLEN-1] = '';
  659.       
  660.       where = ++ptr;
  661.       sstrncpy(workpath,namebuf,sizeof(workpath));
  662.     }
  663.   }
  664.   if(!workpath[0])
  665.     sstrncpy(workpath,"/",sizeof(workpath));
  666.   sstrncpy(buf,workpath,maxlen);  
  667. }
  668. void fs_virtual_path(const char *path, char *buf, int maxlen)
  669. {
  670.   char curpath[MAXPATHLEN],
  671.        workpath[MAXPATHLEN],
  672.        namebuf[MAXPATHLEN],
  673.        *where,*ptr,*last;
  674.   int fini = 1;
  675.   if(!path)
  676.     return;
  677.   if(fs_interpolate(path,curpath,MAXPATHLEN) == -1)
  678.     sstrncpy(curpath,path,MAXPATHLEN);
  679.   if(curpath[0] != '/')
  680.     sstrncpy(workpath,vwd,MAXPATHLEN);
  681.   else
  682.     workpath[0] = '';
  683.   /* curpath is path resolving */
  684.   /* linkpath is path a symlink pointed to */
  685.   /* workpath is the path we've resolved */
  686.   /* main loop */
  687.   while(fini--) {
  688.     where = curpath;
  689.     while(*where != '') {
  690.       if(!strcmp(where,".")) {
  691.         where++;
  692.         continue;
  693.       }
  694.       /* handle "./" */
  695.       if(!strncmp(where,"./",2)) {
  696.         where += 2;
  697.         continue;
  698.       }
  699.       /* handle ".." */
  700.       if(!strcmp(where,"..")) {
  701.         where+=2;
  702.         ptr = last = workpath;
  703.         while(*ptr) {
  704.           if(*ptr == '/')
  705.             last = ptr;
  706.           ptr++;
  707.         }
  708.         *last = '';
  709.         continue;
  710.       }
  711.       /* handle "../" */
  712.       if(!strncmp(where,"../",3)) {
  713.         where += 3;
  714.         ptr = last = workpath;
  715.         while(*ptr) {
  716.           if(*ptr == '/')
  717.             last = ptr;
  718.           ptr++;
  719.         }
  720.         *last = '';
  721.         continue;
  722.       }
  723.       ptr = strchr(where,'/');
  724.       if(!ptr)
  725.         ptr = where + strlen(where) - 1;
  726.       else
  727.         *ptr = '';
  728.       sstrncpy(namebuf,workpath,sizeof(namebuf));
  729.       if(*namebuf) {
  730.         for(last = namebuf; *last; last++) ;
  731.         if(*--last != '/')
  732.           sstrcat(namebuf,"/",MAXPATHLEN);
  733.       } else
  734.         sstrcat(namebuf,"/",MAXPATHLEN);
  735.       sstrcat(namebuf,where,MAXPATHLEN);
  736.       where = ++ptr;
  737.       sstrncpy(workpath,namebuf,sizeof(workpath));
  738.     }
  739.   }
  740.   if(!workpath[0])
  741.     sstrncpy(workpath,"/",sizeof(workpath));
  742.   sstrncpy(buf,workpath,maxlen);  
  743. }
  744. int fs_chdir_canon(const char *path, int hidesymlink)
  745. {
  746.   char resbuf[MAXPATHLEN];
  747.   fsdir_t *f;
  748.   int ret;
  749.   if(fs_resolve_partial(path,resbuf,MAXPATHLEN,FSOP_CHDIR) == -1) {
  750.     errno = ENOENT;
  751.     return -1;
  752.   }
  753.   f = fs_lookup_dir(resbuf,FSOP_CHDIR);
  754.   ret = f->chdir(f,resbuf);
  755.   if(ret != -1) {
  756.     /* chdir succeeded, so we set fs_cwd for future references
  757.      */
  758.      fs_cwd = f;
  759.      sstrncpy(cwd,resbuf,sizeof(cwd));
  760.      if(hidesymlink)
  761.        fs_virtual_path(path,vwd,MAXPATHLEN);
  762.      else
  763.        sstrncpy(vwd,resbuf,sizeof(vwd));
  764.   }
  765.   return ret;  
  766. }
  767. int fs_chdir(const char *path, int hidesymlink)
  768. {
  769.   char resbuf[MAXPATHLEN];
  770.   fsdir_t *f;
  771.   int ret;
  772.   fs_clean_path(path,resbuf,MAXPATHLEN);
  773.   
  774.   f = fs_lookup_dir(path,FSOP_CHDIR);
  775.   ret = f->chdir(f,resbuf);
  776.   if(ret != -1) {
  777.     /* chdir succeeded, so we set fs_cwd for future references
  778.      */
  779.      fs_cwd = f;
  780.      sstrncpy(cwd,resbuf,sizeof(cwd));
  781.      if(hidesymlink)
  782.        fs_virtual_path(path,vwd,MAXPATHLEN);
  783.      else
  784.        sstrncpy(vwd,resbuf,sizeof(vwd));
  785.   }
  786.   return ret;  
  787. }
  788. /* fs_opendir, fs_closedir and fs_readdir all use a nifty 
  789.  * optimization, caching the last-recently-used fsdir_t, and
  790.  * avoid future fsdir_t lookups when iterating via readdir.
  791.  */
  792. void *fs_opendir(const char *path)
  793. {
  794.   fsdir_t *f;
  795.   opendir_t *od;
  796.   DIR *ret;
  797.   if(!strchr(path,'/'))
  798.     f = fs_cwd;
  799.   else {
  800.     char buf[MAXPATHLEN];
  801.     if(fs_resolve_partial(path,buf,MAXPATHLEN,FSOP_OPENDIR) == -1) {
  802.       errno = ENOENT;
  803.       return NULL;
  804.     }
  805.     f = fs_lookup_dir(buf,FSOP_OPENDIR);
  806.   }
  807.   ret = f->opendir(f,path);
  808.   if(!ret)
  809.     return NULL;
  810.   /* cache it here */
  811.   fs_cache_dir = ret;
  812.   fs_cache_fsdir = f;
  813.   od = malloc(sizeof(opendir_t));
  814.   if(!od) {
  815.     f->closedir(f,ret);
  816.     errno = ENOMEM;
  817.     return NULL;
  818.   }
  819.   od->dir = ret;
  820.   od->fsdir = f;
  821.   xaset_insert(opendir_list,(xasetmember_t*)od);
  822.   return ret;
  823. }
  824. static fsdir_t *find_opendir(void *d, int closing)
  825. {
  826.   fsdir_t *f = NULL;
  827.   if(d == fs_cache_dir) {
  828.     f = fs_cache_fsdir;
  829.     if(closing) {
  830.       fs_cache_dir = NULL;
  831.       fs_cache_fsdir = NULL;
  832.     }
  833.   } else {
  834.     opendir_t *od;
  835.     for(od = (opendir_t*)opendir_list->xas_list; od; od=od->next)
  836.       if(od->dir == d) {
  837.         f = od->fsdir;
  838.         break;
  839.       }
  840.     if(closing && od) {
  841.       xaset_remove(opendir_list,(xasetmember_t*)od);
  842.       free(od);
  843.     }
  844.   }
  845.   return f;
  846. }
  847. int fs_closedir(void *d)
  848. {
  849.   fsdir_t *f;
  850.   f = find_opendir(d,1);
  851.   if(!f)
  852.     return -1;
  853.   return f->closedir(f,d);
  854. }
  855. struct dirent *fs_readdir(void *d)
  856. {
  857.   fsdir_t *f;
  858.   f = find_opendir(d,0);
  859.   if(!f)
  860.     return NULL;
  861.   return f->readdir(f,d);
  862. }
  863. /*******************************/
  864. /* all other fs functions here */
  865. /*******************************/
  866. int fs_stat_canon(const char *path, struct stat *sbuf)
  867. {
  868.   fsdir_t *f;
  869.   f = fs_lookup_file_canon(path,NULL,FSOP_STAT);
  870.   return f->stat(f,path,sbuf);
  871. }
  872. int fs_stat(const char *path, struct stat *sbuf)
  873. {
  874.   fsdir_t *f;
  875.   f = fs_lookup_file(path,NULL,FSOP_STAT);
  876.   return f->stat(f,path,sbuf);
  877. }
  878. int fs_lstat_canon(const char *path, struct stat *sbuf)
  879. {
  880.   fsdir_t *f;
  881.   f = fs_lookup_file_canon(path,NULL,FSOP_LSTAT);
  882.   return f->lstat(f,path,sbuf);
  883. }
  884. int fs_lstat(const char *path, struct stat *sbuf)
  885. {
  886.   fsdir_t *f;
  887.   f = fs_lookup_file(path,NULL,FSOP_LSTAT);
  888.   return f->lstat(f,path,sbuf);
  889. }
  890. int fs_readlink_canon(const char *path, char *buf, int maxlen)
  891. {
  892.   fsdir_t *f;
  893.   f = fs_lookup_file_canon(path,NULL,FSOP_READLINK);
  894.   return f->readlink(f,path,buf,maxlen);
  895. }
  896. int fs_readlink(const char *path, char *buf, int maxlen)
  897. {
  898.   fsdir_t *f;
  899.   f = fs_lookup_file(path,NULL,FSOP_READLINK);
  900.   return f->readlink(f,path,buf,maxlen);
  901. }
  902. /* fs_glob() is just a wrapper for glob, setting the various gl_
  903.  * callbacks to our fs functions.
  904.  */
  905. int fs_glob(const char *pattern, int flags,
  906.             int (*errfunc)(const char *, int),
  907.             glob_t *pglob)
  908. {
  909.   if(pglob) {
  910.     flags |= GLOB_ALTDIRFUNC;
  911.     
  912.     pglob->gl_closedir = (void (*)(void*))fs_closedir;
  913.     pglob->gl_readdir = fs_readdir;
  914.     pglob->gl_opendir = fs_opendir;
  915.     pglob->gl_lstat = fs_lstat;
  916.     pglob->gl_stat = fs_stat;
  917.   }
  918.   return glob(pattern,flags,errfunc,pglob);
  919. }
  920. void fs_globfree(glob_t *pglob)
  921. { globfree(pglob); }
  922. int fs_rename_canon(const char *rfrom, const char *rto)
  923. {
  924.   fsdir_t *f;
  925.   f = fs_lookup_file_canon(rfrom,NULL,FSOP_RENAME);
  926.   if(f != fs_lookup_file_canon(rto,NULL,FSOP_RENAME)) {
  927.     errno = EXDEV;
  928.     return -1;
  929.   }
  930.   return f->rename(f,rfrom,rto);
  931. }
  932. int fs_rename(const char *rfrom, const char *rto)
  933. {
  934.   fsdir_t *f;
  935.   f = fs_lookup_file(rfrom,NULL,FSOP_RENAME);
  936.   if(f != fs_lookup_file(rto,NULL,FSOP_RENAME)) {
  937.     errno = EXDEV;
  938.     return -1;
  939.   }
  940.   return f->rename(f,rfrom,rto);
  941. }
  942. int fs_unlink_canon(const char *name)
  943. {
  944.   fsdir_t *f;
  945.   f = fs_lookup_file_canon(name,NULL,FSOP_UNLINK);
  946.   return f->unlink(f,name);
  947. }
  948. int fs_unlink(const char *name)
  949. {
  950.   fsdir_t *f;
  951.   f = fs_lookup_file(name,NULL,FSOP_UNLINK);
  952.   return f->unlink(f,name);
  953. }
  954. fsdir_t *fs_open_canon(const char *name, int flags, int *fd)
  955. {
  956.   char *deref;
  957.   fsdir_t *f;
  958.   int ret;
  959.   f = fs_lookup_file_canon(name,&deref,FSOP_OPEN);
  960.   ret = f->open(f,deref,flags);
  961.   if(ret == -1)
  962.     return NULL;
  963.   if(fd)
  964.     *fd = ret;
  965.   return f;  
  966. }
  967. fsdir_t *fs_open(const char *name, int flags, int *fd)
  968. {
  969.   fsdir_t *f;
  970.   int ret;
  971.   f = fs_lookup_file(name,NULL,FSOP_OPEN);
  972.   ret = f->open(f,name,flags);
  973.   if(ret == -1)
  974.     return NULL;
  975.   if(fd)
  976.     *fd = ret;
  977.   return f;  
  978. }
  979. fsdir_t *fs_creat_canon(const char *name, mode_t mode, int *fd)
  980. {
  981.   char *deref;
  982.   fsdir_t *f;
  983.   int ret;
  984.   f = fs_lookup_file_canon(name,&deref,FSOP_CREAT);
  985.   ret = f->creat(f,deref,mode);
  986.   if(ret == -1)
  987.     return NULL;
  988.   if(fd)
  989.     *fd = ret;
  990.   return f;
  991. }
  992. fsdir_t *fs_creat(const char *name, mode_t mode, int *fd)
  993. {
  994.   fsdir_t *f;
  995.   int ret;
  996.   f = fs_lookup_file(name,NULL,FSOP_CREAT);
  997.   ret = f->creat(f,name,mode);
  998.   if(ret == -1)
  999.     return NULL;
  1000.   if(fd)
  1001.     *fd = ret;
  1002.   return f;
  1003. }
  1004. int fs_close(fsdir_t *f, int fd)
  1005. { return f->close(f,fd); }
  1006. int fs_read(fsdir_t *f, int fd, char *buf, size_t size)
  1007. { return f->read(f,fd,buf,size); }
  1008. int fs_write(fsdir_t *f, int fd, const char *buf, size_t size)
  1009. { return f->write(f,fd,buf,size); }
  1010. off_t fs_lseek(fsdir_t *f, int fd, off_t offset, int whence)
  1011. { return f->lseek(f,fd,offset,whence); }
  1012. int fs_link_canon(const char *lfrom, const char *lto)
  1013. {
  1014.   fsdir_t *f;
  1015.   f = fs_lookup_file_canon(lfrom,NULL,FSOP_LINK);
  1016.   if(f != fs_lookup_file_canon(lto,NULL,FSOP_LINK)) {
  1017.     errno = EXDEV;
  1018.     return -1;
  1019.   }
  1020.   return f->link(f,lfrom,lto);
  1021. }
  1022. int fs_link(const char *lfrom, const char *lto)
  1023. {
  1024.   fsdir_t *f;
  1025.   f = fs_lookup_file(lfrom,NULL,FSOP_LINK);
  1026.   if(f != fs_lookup_file(lto,NULL,FSOP_LINK)) {
  1027.     errno = EXDEV;
  1028.     return -1;
  1029.   }
  1030.   return f->link(f,lfrom,lto);
  1031. }
  1032. int fs_symlink_canon(const char *lfrom, const char *lto)
  1033. {
  1034.   fsdir_t *f;
  1035.   f = fs_lookup_file_canon(lto,NULL,FSOP_SYMLINK);
  1036.   return f->symlink(f,lfrom,lto);
  1037. }
  1038. int fs_symlink(const char *lfrom, const char *lto)
  1039. {
  1040.   fsdir_t *f;
  1041.   f = fs_lookup_file(lto,NULL,FSOP_SYMLINK);
  1042.   return f->symlink(f,lfrom,lto);
  1043. }
  1044. int fs_chmod_canon(const char *name, mode_t mode)
  1045. {
  1046.   char *deref;
  1047.   fsdir_t *f;
  1048.   f = fs_lookup_file_canon(name,&deref,FSOP_CHMOD);
  1049.   return f->chmod(f,deref,mode);
  1050. }
  1051. int fs_chmod(const char *name, mode_t mode)
  1052. {
  1053.   fsdir_t *f;
  1054.   f = fs_lookup_file(name,NULL,FSOP_CHMOD);
  1055.   return f->chmod(f,name,mode);
  1056. }
  1057. int fs_chown_canon(const char *name, uid_t uid, gid_t gid)
  1058. {
  1059.   fsdir_t *f;
  1060.   f = fs_lookup_file_canon(name,NULL,FSOP_CHOWN);
  1061.   return f->chown(f,name,uid,gid);
  1062. }
  1063. int fs_chown(const char *name, uid_t uid, gid_t gid)
  1064. {
  1065.   fsdir_t *f;
  1066.   f = fs_lookup_file(name,NULL,FSOP_CHOWN);
  1067.   return f->chown(f,name,uid,gid);
  1068. }
  1069. /* fs_gets is not truely a vfs function, however it's included here
  1070.  * for simplicity.
  1071.  */
  1072. char *fs_gets(char *buf, size_t size, fsdir_t *f, int fd)
  1073. {
  1074.   char *bp;
  1075.   int toread;
  1076.   static IOBUF *iobuf = NULL;
  1077.   if(!f) {
  1078.     errno = EBADF;
  1079.     return NULL;
  1080.   }
  1081.   
  1082.   if(!iobuf) {
  1083.     iobuf = (IOBUF*)pcalloc(permanent_pool,sizeof(IOBUF));
  1084.     iobuf->buf = iobuf->cp = palloc(permanent_pool,1024);
  1085.     iobuf->left = iobuf->bufsize = 1024;
  1086.   }
  1087.   bp = buf;
  1088.   while(size) {
  1089.     if(!iobuf->cp || iobuf->left == 1024) { /* empty buffer */
  1090.       toread = fs_read(f,fd,iobuf->buf,(size < 1024 ? size : 1024));
  1091.       if(toread <= 0) {
  1092.         if(bp != buf) {
  1093.           *bp = '';
  1094.           return buf;
  1095.         } else
  1096.           return NULL;
  1097.       }
  1098.       iobuf->left = 1024 - toread;
  1099.       iobuf->cp = iobuf->buf;
  1100.     } else
  1101.       toread = 1024 - iobuf->left;
  1102.     while(size && *iobuf->cp != 'n' && toread--) {
  1103.       *bp++ = *iobuf->cp++;
  1104.       size--;
  1105.       iobuf->left++;
  1106.     }
  1107.     if(size && toread && *iobuf->cp == 'n') {
  1108.       size--; toread--;
  1109.       *bp++ = *iobuf->cp++;
  1110.       iobuf->left++;
  1111.       break;
  1112.     }
  1113.     if(!toread)
  1114.       iobuf->cp = NULL;
  1115.   }
  1116.   *bp = '';
  1117.   return buf;
  1118. }
  1119. int init_fs()
  1120. {
  1121.   char cwdbuf[MAXPATHLEN];
  1122.   opendir_list = xaset_create(permanent_pool,NULL);
  1123.   fs_std = fs_register(NULL);
  1124.   if(getcwd(cwdbuf,MAXPATHLEN))
  1125.     fs_setcwd(cwdbuf);
  1126.   else {
  1127.     chdir("/");
  1128.     fs_setcwd("/");
  1129.   }
  1130.   
  1131.   return 0;
  1132. }