rewire zip packages inside the virtual filesystem

* change priority of zip packages from "highest" to "just below home dir"
* strict naming rules for zip packages
* require all zips to be below directory "mods/"
* only allow zips to intoduce files below "packages/" (or with a special
  zip package name also below "config/")
* make zip package config persistent (saved to init.cfg)

WARNING: intermediate commit - autodownload system defunct!

DOCREF (removed) addzip removezip
This commit is contained in:
ac-stef
2015-09-07 14:53:56 +02:00
parent 0dab3ade48
commit 816aed4ba6
9 changed files with 208 additions and 97 deletions
+165 -64
View File
@@ -44,35 +44,24 @@ struct ziparchive;
struct zipfile // file in a zip archive
{
char *name;
char *name, *fullname; // name from zip, full path name
uint header, offset, size, compressedsize;
ziparchive *location;
zipfile() : name(NULL), header(0), offset(~0U), size(0), compressedsize(0)
{
}
~zipfile()
{
DELETEA(name);
}
zipfile() : name(NULL), fullname(NULL), header(0), offset(~0U), size(0), compressedsize(0), location(NULL) { }
~zipfile() { DELSTRING(name); DELSTRING(fullname); }
};
struct ziparchive // zip archive file
{
char *name;
hashtable<const char *, zipfile> files;
stream *data;
vector<zipfile> files;
int openfiles;
int type;
zipstream *owner;
ziparchive() : name(NULL), files(512), data(NULL), openfiles(0), type(-1), owner(NULL)
{
}
~ziparchive()
{
DELETEA(name);
DELETEP(data);
}
ziparchive() : name(NULL), data(NULL), openfiles(0), owner(NULL) { }
~ziparchive() { DELSTRING(name); DELETEP(data); }
};
static bool findzipdirectory(stream *f, zipdirectoryheader &hdr)
@@ -195,8 +184,7 @@ static bool readlocalfileheader(stream *f, ziplocalfileheader &h, uint offset)
return true;
}
static vector<ziparchive *> archives;
#if 0
ziparchive *findzip(const char *name)
{
loopv(archives) if(!strcmp(name, archives[i]->name)) return archives[i];
@@ -355,6 +343,9 @@ bool removezip(const char *name)
delete exists;
return true;
}
#endif
// stream interface
struct zipstream : stream
{
@@ -554,68 +545,178 @@ struct zipstream : stream
}
};
bool extractzipfile(ziparchive *a, zipfile *f, const char *name)
// manage zips as part of the AC virtual filesystem
static vector<ziparchive *> archives; // list of mounted zip files, highest priority first
static hashtable<const char *, zipfile *> zipfiles; // table of all files in all mounted zips
static void mountzip(ziparchive &arch, const char *mountdir, const char *stripdir, bool allowconfig)
{
zipstream *s = new zipstream;
FILE *target = NULL;
defformatstring(fname)("%s", findfile(name, "wb"));
bool error = false;
if(s->open(a, f) && (target = fopen(fname, "wb")))
string mdir = "", fname;
if(mountdir)
{
size_t len;
uchar copybuf[1024];
while((len = s->read(copybuf, 1024))) fwrite(copybuf, 1, len, target);
copystring(mdir, mountdir);
if(fixpackagedir(mdir) <= 1) mdir[0] = '\0';
}
else error = true;
if(error) conoutf("failed to extract zip file %s from archive %s", fname, a->name);
if(target) fclose(target);
delete s;
return !error;
int striplen = stripdir ? (int)strlen(stripdir) : 0;
loopv(arch.files)
{ // build full path names - and check them
zipfile *zf = &arch.files[i];
formatstring(fname)("%s%s", mdir, striplen && !strncmp(zf->name, stripdir, striplen) ? zf->name + striplen : zf->name);
path(fname);
unixpath(fname);
zf->fullname = newstring(fname);
zf->location = &arch;
filtertext(fname, fname, FTXT__MEDIAFILEPATH);
if(strcmp(fname, zf->fullname) || (strncmp("packages/", fname, 9) && (!allowconfig || strncmp("config/", fname, 7))))
{ // path is not allowed (illegal characters or illegal path)
#if defined(_DEBUG) && !defined(STANDALONE)
clientlogf("mountzip: ignored file %s from zip %s", fname, arch.name);
#endif
arch.files.remove(i--).~zipfile();
}
else path(zf->fullname);
}
}
static void rebuildzipfilehashtable()
{
zipfiles.clear(false);
loopv(archives)
{
ziparchive *arch = archives[i];
loopvj(arch->files)
{
zipfile *zf = &arch->files[j];
zipfiles.access(zf->fullname, zf);
}
}
}
// manage zipped mod packages in subdirectory "mods/" including description and preview images
// zip packages are restricted to contain files below "packages/" only - unless the zip filename starts with "###", which additionally allows files below "config/"
ziparchive *findzip(const char *name)
{
loopv(archives) if(!strcmp(name, archives[i]->name)) return archives[i];
return NULL;
}
#ifndef STANDALONE
const char *fixzipname(char *name) // check, if valid path + filename, based on mods/, return NULL if invalid
{
string tmp;
unixpath(name); // changes "name"!
filtertext(tmp, name, FTXT__MEDIAFILEPATH);
const char *bp = behindpath(tmp), *pd = parentdir(tmp);
static defformatstring(pname)("%s%s%s%s", pd, bp != tmp ? "/" : "", !strncmp("###", behindpath(name), 3) ? "###" : "", bp);
if(strlen(name) > 40 || strchr(name, '.') || strcmp(pname, name) || *name == '/') return NULL; // illegal filename
formatstring(pname)("mods/%s.zip", name);
return path(pname);
}
VAR(zipcachemaxsize, 0, 512, 1024); // load zip files smaller than 512K straight into RAM
void addzipmod(char *name)
{
const char *pname = fixzipname(name);
if(!pname)
{
conoutf("addzipmod: illegal filename %s", name);
return;
}
ziparchive *exists = findzip(name);
if(exists)
{ // already added zip
archives.removeobj(exists);
archives.add(exists); // sort to the end of the list
rebuildzipfilehashtable();
return;
}
ziparchive *arch = new ziparchive;
arch->name = newstring(name);
if(!(arch->data = openfile(pname, "rb")))
{
conoutf("could not open file %s", pname);
delete arch;
return;
}
int zipsize = arch->data->size();
if(zipsize > 0 && zipsize < zipcachemaxsize * 1024)
{
uchar *buf = new uchar[zipsize];
if(arch->data->read(buf, zipsize) != zipsize)
{
conoutf("could not cache file %s", pname);
delete[] buf;
delete arch;
return;
}
delete arch->data;
arch->data = openmemfile(buf, zipsize, NULL);
}
zipdirectoryheader h;
if(!findzipdirectory(arch->data, h) || !readzipdirectory(arch->data, h.entries, h.offset, h.size, arch->files))
{
conoutf("could not read directory in zip %s", pname);
delete arch;
return;
}
mountzip(*arch, NULL, NULL, !strncmp(behindpath(name), "###", 3));
archives.add(arch);
rebuildzipfilehashtable();
clientlogf("added zipmod %s, %d bytes, %d files", pname, zipsize, arch->files.length());
}
COMMAND(addzipmod, "s");
void writezipmodconfig(stream *f)
{
f->printf("zipcachemaxsize %d\n", zipcachemaxsize);
loopv(archives) f->printf("addzipmod %s\n", archives[i]->name);
}
#endif
// AC virtual filesystem
bool findzipfile(const char *name)
{
return zipfiles.access(name) != NULL;
}
stream *openzipfile(const char *name, const char *mode)
{
for(; *mode; mode++) if(*mode=='w' || *mode=='a') return NULL;
loopvrev(archives)
// if(!strncmp(name, "zip://", 6)) name += 6;
zipfile **zf = zipfiles.access(name);
if(zf && *zf)
{
ziparchive *arch = archives[i];
zipfile *f = arch->files.access(name);
if(!f) continue;
zipstream *s = new zipstream;
if(s->open(arch, f)) return s;
if(s->open((*zf)->location, *zf)) return s;
delete s;
}
return NULL;
}
int listzipfiles(const char *dir, const char *ext, vector<char *> &files)
void listzipfiles(const char *dir, const char *ext, vector<char *> &files) // (does not list memfiles)
{
int extsize = ext ? (int)strlen(ext)+1 : 0, dirsize = (int)strlen(dir), dirs = 0;
loopvrev(archives)
int extsize = ext ? (int)strlen(ext)+1 : 0, dirsize = (int)strlen(dir);
enumerate(zipfiles, zipfile *, zf,
{
ziparchive *arch = archives[i];
int oldsize = files.length();
enumerate(arch->files, zipfile, f,
if(strncmp(zf->fullname, dir, dirsize)) continue;
const char *name = zf->fullname + dirsize;
if(name[0] == PATHDIV) name++;
if(strchr(name, PATHDIV)) continue;
if(!ext) files.add(newstring(name));
else
{
if(strncmp(f.name, dir, dirsize)) continue;
const char *name = f.name + dirsize;
if(name[0] == PATHDIV) name++;
if(strchr(name, PATHDIV)) continue;
if(!ext) files.add(newstring(name));
else
{
int namelength = (int)strlen(name) - extsize;
if(namelength > 0 && name[namelength] == '.' && strncmp(name+namelength+1, ext, extsize-1)==0)
files.add(newstring(name, namelength));
}
});
if(files.length() > oldsize) dirs++;
}
return dirs;
int namelength = (int)strlen(name) - extsize;
if(namelength > 0 && name[namelength] == '.' && strncmp(name+namelength+1, ext, extsize-1)==0)
files.add(newstring(name, namelength));
}
});
}
#ifndef STANDALONE
COMMANDF(addzip, "sss", (const char *name, const char *mount, const char *strip) { addzip(name, mount[0] ? mount : NULL, strip[0] ? strip : NULL); });
COMMANDF(removezip, "s", (const char *name) { removezip(name); });
#endif