/*
* Rather than reading /adm/users, which is a lot of work for
* a toy program, we assume all groups have the form
* NNN:user:user:
* meaning that each user is the leader of his own group.
*/
enum
{
OPERM = 0x3, /* mask of all permission types in open mode */
Nram = 512,
Maxsize = 512*1024*1024,
Maxfdata = 8192,
};
typedef struct Fid Fid;
typedef struct Ram Ram;
struct Fid
{
short busy;
short open;
short rclose;
int fid;
Fid *next;
char *user;
Ram *ram;
};
struct Ram
{
short busy;
short open;
long parent; /* index in Ram array */
Qid qid;
long perm;
char *name;
ulong atime;
ulong mtime;
char *user;
char *group;
char *muid;
char *data;
long ndata;
};
ulong path; /* incremented for each new file */
Fid *fids;
Ram ram[Nram];
int nram;
int mfd[2];
char *user;
uchar mdata[IOHDRSZ+Maxfdata];
uchar rdata[Maxfdata]; /* buffer for data in reply */
uchar statbuf[STATMAX];
Fcall thdr;
Fcall rhdr;
int messagesize = sizeof mdata;
void
ramfsmain(int argc, char *argv[])
{
Ram *r;
char *defmnt;
int p[2];
char buf[32];
int fd, srvfd;
int stdio = 0;
srvfd = -1;
defmnt = "/tmp";
ARGBEGIN{
case 'D':
debug = 1;
break;
case 'i': /* this is DIFFERENT from normal ramfs; use 1 for both for kernel */
defmnt = 0;
stdio = 1;
srvfd = 0;
mfd[0] = 1;
mfd[1] = 1;
break;
case 's':
defmnt = 0;
break;
case 'm':
defmnt = ARGF();
break;
default:
ramfsusage();
}ARGEND
/*
* To change length, must have write permission on file.
*/
#ifdef CHECKS
if(dir.length!=~0 && dir.length!=r->ndata){
if(!perm(f, r, Pwrite))
return Eperm;
}
#endif
/*
* To change name, must have write permission in parent
* and name must be unique.
*/
if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
#ifdef CHECKS
if(!perm(f, &ram[r->parent], Pwrite))
return Eperm;
#endif
for(s=ram; s<&ram[nram]; s++)
if(s->busy && s->parent==r->parent)
if(strcmp(dir.name, s->name)==0)
return Eexist;
}
#ifdef OWNERS
/*
* To change mode, must be owner or group leader.
* Because of lack of users file, leader=>group itself.
*/
if(dir.mode!=~0 && r->perm!=dir.mode){
if(strcmp(f->user, r->user) != 0)
if(strcmp(f->user, r->group) != 0)
return Enotowner;
}
/*
* To change group, must be owner and member of new group,
* or leader of current group and leader of new group.
* Second case cannot happen, but we check anyway.
*/
if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
if(strcmp(f->user, r->user) == 0)
if(strcmp(f->user, dir.gid) == 0)
goto ok;
if(strcmp(f->user, r->group) == 0)
if(strcmp(f->user, dir.gid) == 0)
goto ok;
return Enotowner;
ok:;
}
#endif
/* all ok; do it */
if(dir.mode != ~0){
dir.mode &= ~DMDIR; /* cannot change dir bit */
dir.mode |= r->perm&DMDIR;
r->perm = dir.mode;
}
if(dir.name[0] != '\0'){
free(r->name);
r->name = estrdup(dir.name);
}
if(dir.gid[0] != '\0')
r->group = atom(dir.gid);
/*
* Custom allocators to avoid malloc overheads on small objects.
* We never free these. (See below.)
*/
typedef struct Stringtab Stringtab;
struct Stringtab {
Stringtab *link;
char *str;
};
static Stringtab*
taballoc(void)
{
static Stringtab *t;
static uint nt;
if(nt == 0){
t = malloc(64*sizeof(Stringtab));
if(t == 0)
sysfatal("out of memory");
nt = 64;
}
nt--;
return t++;
}
static char*
xstrdup(char *s)
{
char *r;
int len;
static char *t;
static int nt;
len = strlen(s)+1;
if(len >= 8192)
sysfatal("strdup big string");
if(nt < len){
t = malloc(8192);
if(t == 0)
sysfatal("out of memory");
nt = 8192;
}
r = t;
t += len;
nt -= len;
strcpy(r, s);
return r;
}
/*
* Return a uniquely allocated copy of a string.
* Don't free these -- they stay in the table for the
* next caller who wants that particular string.
* String comparison can be done with pointer comparison
* if you know both strings are atoms.
*/
static Stringtab *stab[1024];
static uint
hash(char *s)
{
uint h;
uchar *p;
h = 0;
for(p=(uchar*)s; *p; p++)
h = h*37 + *p;
return h;
}