plan9port/src/cmd/acme/disk.c
Russ Cox 67dbeee5fe acme: check file content before declaring file "modified since last read"
Bad remote file systems can change mtime unexpectedly,
and then there is the problem that git rebase and similar
operations like to change the files and then change them back,
modifying the mtimes but not the content.

Avoid spurious Put errors on both of those by checking file
content.

(False positive "modified since last read" make the real ones
difficult to notice.)
2017-10-10 13:51:24 -04:00

133 lines
2.2 KiB
C

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include <libsec.h>
#include "dat.h"
#include "fns.h"
static Block *blist;
int
tempfile(void)
{
char buf[128];
int i, fd;
snprint(buf, sizeof buf, "/tmp/X%d.%.4sacme", getpid(), getuser());
for(i='A'; i<='Z'; i++){
buf[5] = i;
if(access(buf, AEXIST) == 0)
continue;
fd = create(buf, ORDWR|ORCLOSE|OCEXEC, 0600);
if(fd >= 0)
return fd;
}
return -1;
}
Disk*
diskinit()
{
Disk *d;
d = emalloc(sizeof(Disk));
d->fd = tempfile();
if(d->fd < 0){
fprint(2, "acme: can't create temp file: %r\n");
threadexitsall("diskinit");
}
return d;
}
static
uint
ntosize(uint n, uint *ip)
{
uint size;
if(n > Maxblock)
error("internal error: ntosize");
size = n;
if(size & (Blockincr-1))
size += Blockincr - (size & (Blockincr-1));
/* last bucket holds blocks of exactly Maxblock */
if(ip)
*ip = size/Blockincr;
return size * sizeof(Rune);
}
Block*
disknewblock(Disk *d, uint n)
{
uint i, j, size;
Block *b;
size = ntosize(n, &i);
b = d->free[i];
if(b)
d->free[i] = b->u.next;
else{
/* allocate in chunks to reduce malloc overhead */
if(blist == nil){
blist = emalloc(100*sizeof(Block));
for(j=0; j<100-1; j++)
blist[j].u.next = &blist[j+1];
}
b = blist;
blist = b->u.next;
b->addr = d->addr;
if(d->addr+size < d->addr){
error("temp file overflow");
}
d->addr += size;
}
b->u.n = n;
return b;
}
void
diskrelease(Disk *d, Block *b)
{
uint i;
ntosize(b->u.n, &i);
b->u.next = d->free[i];
d->free[i] = b;
}
void
diskwrite(Disk *d, Block **bp, Rune *r, uint n)
{
int size, nsize;
Block *b;
b = *bp;
size = ntosize(b->u.n, nil);
nsize = ntosize(n, nil);
if(size != nsize){
diskrelease(d, b);
b = disknewblock(d, n);
*bp = b;
}
if(pwrite(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune))
error("write error to temp file");
b->u.n = n;
}
void
diskread(Disk *d, Block *b, Rune *r, uint n)
{
if(n > b->u.n)
error("internal error: diskread");
ntosize(b->u.n, nil);
if(pread(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune))
error("read error from temp file");
}