summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2021-10-23 13:40:06 +0000
committercinap_lenrek <cinap_lenrek@felloff.net>2021-10-23 13:40:06 +0000
commitdb971a6189e63802b4c7c0ee41bf00e9864f52a7 (patch)
treeb211a3e5ae91db86a2099019e323eaeb2a63cb77
parente54b6c6cbd4d82d70ddb4932aeafb0b028cd71f5 (diff)
kernel: fix stat bugs
In a few places, we where using a fixed buffer of sizeof(Dir)+100 size for stat. This is not correct and fails if the name returned in stat is long. This results in being unable to seek to the end of file with a long filename. The kernel should do the same thing as dirfstat() from libc; handling the conversion and buffer allocation and returning a freeable Dir* pointer. For this, a new dirchanstat() function was added. The fstat syscall was not rewriting the name to the last path element; fix it. In addition, gracefully handle the mountfix case, reallocating the buffer to accomidate the required stat length plus size of the new name so dirsetname() does not fail.
-rw-r--r--sys/src/9/port/chan.c39
-rw-r--r--sys/src/9/port/devfs.c25
-rw-r--r--sys/src/9/port/devswap.c17
-rw-r--r--sys/src/9/port/portfns.h1
-rw-r--r--sys/src/9/port/sdloop.c17
-rw-r--r--sys/src/9/port/sysfile.c112
6 files changed, 112 insertions, 99 deletions
diff --git a/sys/src/9/port/chan.c b/sys/src/9/port/chan.c
index 960c1d232..e69e5d7b6 100644
--- a/sys/src/9/port/chan.c
+++ b/sys/src/9/port/chan.c
@@ -1720,3 +1720,42 @@ isdir(Chan *c)
return;
error(Enotdir);
}
+
+
+enum
+{
+ DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
+};
+
+Dir*
+dirchanstat(Chan *c)
+{
+ Dir *d;
+ uchar *buf;
+ int n, nd, i;
+
+ nd = DIRSIZE;
+ for(i=0; i<2; i++){ /* should work by the second try */
+ d = smalloc(sizeof(Dir) + BIT16SZ + nd);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ buf = (uchar*)&d[1];
+ n = devtab[c->type]->stat(c, buf, BIT16SZ+nd);
+ if(n < BIT16SZ)
+ error(Eshortstat);
+ nd = GBIT16(buf); /* upper bound on size of Dir + strings */
+ if(nd <= n){
+ if(convM2D(buf, n, d, (char*)&d[1]) == 0)
+ error(Eshortstat);
+ poperror();
+ return d;
+ }
+ /* else sizeof(Dir)+BIT16SZ+nd is plenty */
+ free(d);
+ poperror();
+ }
+ error(Eshortstat);
+ return nil;
+}
diff --git a/sys/src/9/port/devfs.c b/sys/src/9/port/devfs.c
index 4a75f9c76..3607069b2 100644
--- a/sys/src/9/port/devfs.c
+++ b/sys/src/9/port/devfs.c
@@ -553,18 +553,6 @@ parsename(char *name, char *disk, char **tree, char **dev)
validname(*dev, 0);
}
-static vlong
-getlen(Chan *c)
-{
- uchar buf[128]; /* old DIRLEN plus a little should be plenty */
- Dir d;
- long l;
-
- l = devtab[c->type]->stat(c, buf, sizeof buf);
- convM2D(buf, l, &d, nil);
- return d.length;
-}
-
/*
* Process a single line of configuration,
* often of the form "cmd newname idev0 idev1".
@@ -602,8 +590,6 @@ mconfig(char* a, long n)
start = 0;
mp = nil;
cb = nil;
- idev = nil;
- ilen = nil;
keylen = 0;
if(waserror()){
@@ -686,6 +672,8 @@ mconfig(char* a, long n)
*/
poperror();
rlock(&lck);
+ idev = smalloc(sizeof(Chan*) * Ndevs);
+ ilen = smalloc(sizeof(vlong) * Ndevs);
if(waserror()){
runlock(&lck);
Fail:
@@ -699,11 +687,14 @@ Fail:
free(cb);
nexterror();
}
- idev = smalloc(sizeof(Chan*) * Ndevs);
- ilen = smalloc(sizeof(vlong) * Ndevs);
for(i = 1; i < cb->nf; i++){
+ Dir *dir;
+
idev[i-1] = namec(cb->f[i], Aopen, ORDWR, 0);
- ilen[i-1] = getlen(idev[i-1]);
+
+ dir = dirchanstat(idev[i-1]);
+ ilen[i-1] = dir->length;
+ free(dir);
}
poperror();
runlock(&lck);
diff --git a/sys/src/9/port/devswap.c b/sys/src/9/port/devswap.c
index fe872057e..d0d8156b9 100644
--- a/sys/src/9/port/devswap.c
+++ b/sys/src/9/port/devswap.c
@@ -383,10 +383,6 @@ needpages(void*)
static void
setswapchan(Chan *c)
{
- uchar buf[sizeof(Dir)+100];
- Dir d;
- int n;
-
if(waserror()){
cclose(c);
nexterror();
@@ -403,16 +399,17 @@ setswapchan(Chan *c)
* to be at most the size of the partition
*/
if(devtab[c->type]->dc != L'M'){
- n = devtab[c->type]->stat(c, buf, sizeof buf);
- if(n <= 0 || convM2D(buf, n, &d, nil) == 0)
- error("stat failed in setswapchan");
- if(d.length < (vlong)conf.nswppo*BY2PG)
+ Dir *d = dirchanstat(c);
+ if(d->length < (vlong)conf.nswppo*BY2PG){
+ free(d);
error("swap device too small");
- if(d.length < (vlong)conf.nswap*BY2PG){
- conf.nswap = d.length/BY2PG;
+ }
+ if(d->length < (vlong)conf.nswap*BY2PG){
+ conf.nswap = d->length/BY2PG;
swapalloc.top = &swapalloc.swmap[conf.nswap];
swapalloc.free = conf.nswap;
}
+ free(d);
}
c->flag &= ~CCACHE;
cclunk(c);
diff --git a/sys/src/9/port/portfns.h b/sys/src/9/port/portfns.h
index 99da20d10..d2345c508 100644
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -79,6 +79,7 @@ void devshutdown(void);
int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*);
Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*);
int devwstat(Chan*, uchar*, int);
+Dir* dirchanstat(Chan *);
void drawactive(int);
void drawcmap(void);
void dumpaproc(Proc*);
diff --git a/sys/src/9/port/sdloop.c b/sys/src/9/port/sdloop.c
index 49ea6886d..5b3419cef 100644
--- a/sys/src/9/port/sdloop.c
+++ b/sys/src/9/port/sdloop.c
@@ -47,21 +47,14 @@ SDifc sdloopifc;
static void
identify(Ctlr *c, SDunit *u)
{
- int n;
uvlong s, osectors;
- uchar buf[sizeof(Dir) + 100];
- Dir dir;
+ Dir *dir;
- if(waserror()){
- iprint("sdloop: identify: %s\n", up->errstr);
- nexterror();
- }
osectors = c->sectors;
- n = devtab[c->c->type]->stat(c->c, buf, sizeof buf);
- if(convM2D(buf, n, &dir, nil) == 0)
- error("internal error: stat error in seek");
- s = dir.length / c->sectsize;
- poperror();
+
+ dir = dirchanstat(c->c);
+ s = dir->length / c->sectsize;
+ free(dir);
memset(u->inquiry, 0, sizeof u->inquiry);
u->inquiry[2] = 2;
diff --git a/sys/src/9/port/sysfile.c b/sys/src/9/port/sysfile.c
index 54cb64d02..1ead500a7 100644
--- a/sys/src/9/port/sysfile.c
+++ b/sys/src/9/port/sysfile.c
@@ -592,22 +592,26 @@ mountfix(Chan *c, uchar *op, long n, long maxn)
}
runlock(&mh->lock);
+ if(waserror())
+ goto Norewrite;
name = dirname(p, &nname);
- /*
- * Do the stat but fix the name. If it fails, leave old entry.
- * BUG: If it fails because there isn't room for the entry,
- * what can we do? Nothing, really. Might as well skip it.
- */
if(buf == nil){
nbuf = 4096;
buf = smalloc(nbuf);
}
- if(waserror())
- goto Norewrite;
l = devtab[nc->type]->stat(nc, buf, nbuf);
+ if(l < BIT16SZ)
+ error(Eshortstat);
+ rest = BIT16SZ + GBIT16(buf) + nname;
+ if(rest > nbuf){
+ free(buf);
+ nbuf = rest;
+ buf = smalloc(nbuf);
+ l = devtab[nc->type]->stat(nc, buf, nbuf);
+ }
l = dirsetname(name, nname, buf, l, nbuf);
- if(l == BIT16SZ)
- error("dirsetname");
+ if(l <= BIT16SZ)
+ error(Eshortstat);
poperror();
/*
@@ -831,10 +835,8 @@ syspwrite(va_list list)
static vlong
sseek(int fd, vlong o, int type)
{
+ Dir *d;
Chan *c;
- uchar buf[sizeof(Dir)+100];
- Dir dir;
- int n;
vlong off;
c = fdtochan(fd, -1, 1, 1);
@@ -872,10 +874,9 @@ sseek(int fd, vlong o, int type)
case 2:
if(c->qid.type & QTDIR)
error(Eisdir);
- n = devtab[c->type]->stat(c, buf, sizeof buf);
- if(convM2D(buf, n, &dir, nil) == 0)
- error("internal error: stat error in seek");
- off = dir.length + o;
+ d = dirchanstat(c);
+ off = d->length + o;
+ free(d);
if(off < 0)
error(Enegoff);
c->offset = off;
@@ -967,34 +968,36 @@ pathlast(Path *p)
uintptr
sysfstat(va_list list)
{
+ char *name;
+ uchar *s;
Chan *c;
int fd;
- uint l;
- uchar *s;
+ uint l, r;
fd = va_arg(list, int);
s = va_arg(list, uchar*);
l = va_arg(list, uint);
validaddr((uintptr)s, l, 1);
-
c = fdtochan(fd, -1, 0, 1);
if(waserror()) {
cclose(c);
nexterror();
}
- l = devtab[c->type]->stat(c, s, l);
+ r = devtab[c->type]->stat(c, s, l);
+ if((name = pathlast(c->path)) != nil)
+ r = dirsetname(name, strlen(name), s, r, l);
poperror();
cclose(c);
- return l;
+ return r;
}
uintptr
sysstat(va_list list)
{
char *name;
+ uchar *s;
Chan *c;
uint l, r;
- uchar *s;
name = va_arg(list, char*);
s = va_arg(list, uchar*);
@@ -1007,10 +1010,8 @@ sysstat(va_list list)
nexterror();
}
r = devtab[c->type]->stat(c, s, l);
- name = pathlast(c->path);
- if(name != nil)
+ if((name = pathlast(c->path)) != nil)
r = dirsetname(name, strlen(name), s, r, l);
-
poperror();
cclose(c);
return r;
@@ -1330,12 +1331,10 @@ packoldstat(uchar *buf, Dir *d)
uintptr
sys_stat(va_list list)
{
- static char old[] = "old stat system call - recompile";
+ char *name;
+ uchar *s;
Chan *c;
- uint l;
- uchar *s, buf[128]; /* old DIRLEN plus a little should be plenty */
- char strs[128], *name;
- Dir d;
+ Dir *d;
name = va_arg(list, char*);
s = va_arg(list, uchar*);
@@ -1346,18 +1345,16 @@ sys_stat(va_list list)
cclose(c);
nexterror();
}
- l = devtab[c->type]->stat(c, buf, sizeof buf);
- /* buf contains a new stat buf; convert to old. yuck. */
- if(l <= BIT16SZ) /* buffer too small; time to face reality */
- error(old);
- name = pathlast(c->path);
- if(name != nil)
- l = dirsetname(name, strlen(name), buf, l, sizeof buf);
- l = convM2D(buf, l, &d, strs);
- if(l == 0)
- error(old);
- packoldstat(s, &d);
-
+ d = dirchanstat(c);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ if((name = pathlast(c->path)) != nil)
+ d->name = name;
+ packoldstat(s, d);
+ poperror();
+ free(d);
poperror();
cclose(c);
return 0;
@@ -1366,13 +1363,10 @@ sys_stat(va_list list)
uintptr
sys_fstat(va_list list)
{
- static char old[] = "old fstat system call - recompile";
- Chan *c;
char *name;
- uint l;
- uchar *s, buf[128]; /* old DIRLEN plus a little should be plenty */
- char strs[128];
- Dir d;
+ uchar *s;
+ Chan *c;
+ Dir *d;
int fd;
fd = va_arg(list, int);
@@ -1383,18 +1377,16 @@ sys_fstat(va_list list)
cclose(c);
nexterror();
}
- l = devtab[c->type]->stat(c, buf, sizeof buf);
- /* buf contains a new stat buf; convert to old. yuck. */
- if(l <= BIT16SZ) /* buffer too small; time to face reality */
- error(old);
- name = pathlast(c->path);
- if(name != nil)
- l = dirsetname(name, strlen(name), buf, l, sizeof buf);
- l = convM2D(buf, l, &d, strs);
- if(l == 0)
- error(old);
- packoldstat(s, &d);
-
+ d = dirchanstat(c);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ if((name = pathlast(c->path)) != nil)
+ d->name = name;
+ packoldstat(s, d);
+ poperror();
+ free(d);
poperror();
cclose(c);
return 0;