WvStreams
unifilesystemgen.cc
00001 #include "unifilesystemgen.h"
00002 #include "wvfile.h"
00003 #include "wvdiriter.h"
00004 #include "wvfileutils.h"
00005 #include "wvmoniker.h"
00006 #include "wvlinkerhack.h"
00007 #include <sys/types.h>
00008 #include <sys/stat.h>
00009 #include <unistd.h>
00010 #include <fcntl.h>
00011 
00012 WV_LINK(UniFileSystemGen);
00013 
00014 
00015 static IUniConfGen *creator(WvStringParm s, IObject *)
00016 {
00017     return new UniFileSystemGen(s, 0777);
00018 }
00019 
00020 WvMoniker<IUniConfGen> UniFileSystemGenMoniker("fs", creator);
00021 
00022 
00023 UniFileSystemGen::UniFileSystemGen(WvStringParm _dir, mode_t _mode)
00024     : dir(_dir), mode(_mode)
00025 {
00026 }
00027 
00028 
00029 static bool key_safe(const UniConfKey &key)
00030 {
00031     UniConfKey::Iter i(key);
00032     for (i.rewind(); i.next(); )
00033     {
00034         if (*i == "." || *i == ".." || *i == "")
00035             return false; // unsafe key segments
00036     }
00037     
00038     // otherwise a safe filename
00039     return true;
00040 }
00041 
00042 
00043 WvString UniFileSystemGen::get(const UniConfKey &key)
00044 {
00045     WvString null;
00046     
00047     if (!key_safe(key))
00048         return null;
00049     
00050     WvString path("%s/%s", dir, key);
00051     
00052     // WARNING: this code depends on the ability to open() a directory
00053     // as long as we don't read it, because we want to fstat() it after.
00054     WvFile file(path, O_RDONLY);
00055     if (!file.isok())
00056         return null; // unreadable; pretend it doesn't exist
00057     
00058     struct stat st;
00059     if (fstat(file.getrfd(), &st) < 0)
00060         return null; // openable but can't stat?  That's odd.
00061 
00062     if (S_ISREG(st.st_mode))
00063     {
00064         WvDynBuf buf;
00065         while (file.isok())
00066             file.read(buf, 4096);
00067         if (file.geterr())
00068             return null;
00069         else
00070             return buf.getstr();
00071     }
00072     else
00073         return ""; // exists, but pretend it's an empty file
00074 }
00075 
00076 
00077 void UniFileSystemGen::set(const UniConfKey &key, WvStringParm value)
00078 {
00079     if (!key_safe(key))
00080         return;
00081     
00082     WvString base("%s/%s", dir, key.removelast(1));
00083     WvString path("%s/%s", dir, key);
00084     
00085     mkdirp(base, mode);
00086     
00087     if (value.isnull())
00088         rm_rf(path);
00089     else
00090     {
00091         WvFile file(path, O_WRONLY|O_CREAT|O_TRUNC, mode & 0666);
00092         file.write(value);
00093     }
00094 }
00095 
00096 
00097 void UniFileSystemGen::setv(const UniConfPairList &pairs)
00098 {
00099     setv_naive(pairs);
00100 }
00101 
00102 
00103 class UniFileSystemGenIter : public UniConfGen::Iter
00104 {
00105 private:
00106     UniFileSystemGen *gen;
00107     WvDirIter i;
00108     UniConfKey rel;
00109     
00110 public:
00111     UniFileSystemGenIter(UniFileSystemGen *_gen, WvStringParm path,
00112                          const UniConfKey &_rel)
00113         : gen(_gen), i(path, false), rel(_rel)
00114         { }
00115 
00116     ~UniFileSystemGenIter()
00117         { }
00118 
00119     void rewind()
00120         { i.rewind(); }
00121 
00122     bool next() 
00123         { return i.next(); }
00124 
00125     UniConfKey key() const
00126         { return i->relname; }
00127 
00128     WvString value() const
00129         { return gen->get(WvString("%s/%s", rel, i->relname)); }
00130 };
00131 
00132 
00133 UniConfGen::Iter *UniFileSystemGen::iterator(const UniConfKey &key)
00134 {
00135     if (!key_safe(key))
00136         return NULL;
00137     
00138     return new UniFileSystemGenIter(this, WvString("%s/%s", dir, key), key);
00139 }