URI: 
       tacme: check file content before declaring file "modified since last read" - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 67dbeee5fe9e2a855384048615bafe100e876f4c
   DIR parent 680c57a15c51c302d89aec134e25f08820d3f30d
  HTML Author: Russ Cox <rsc@swtch.com>
       Date:   Tue, 10 Oct 2017 13:49:37 -0400
       
       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.)
       
       Diffstat:
         M src/cmd/acme/acme.c                 |       1 +
         M src/cmd/acme/addr.c                 |       1 +
         M src/cmd/acme/buff.c                 |       9 ++++++---
         M src/cmd/acme/cols.c                 |       1 +
         M src/cmd/acme/dat.h                  |       7 ++++---
         M src/cmd/acme/disk.c                 |       1 +
         M src/cmd/acme/ecmd.c                 |       3 ++-
         M src/cmd/acme/edit.c                 |       1 +
         M src/cmd/acme/elog.c                 |       1 +
         M src/cmd/acme/exec.c                 |      37 +++++++++++++++++++++++++++++--
         M src/cmd/acme/file.c                 |       5 +++--
         M src/cmd/acme/fns.h                  |       2 +-
         M src/cmd/acme/fsys.c                 |       1 +
         M src/cmd/acme/logf.c                 |       1 +
         M src/cmd/acme/look.c                 |       1 +
         M src/cmd/acme/regx.c                 |       1 +
         M src/cmd/acme/rows.c                 |       1 +
         M src/cmd/acme/scrl.c                 |       1 +
         M src/cmd/acme/text.c                 |      13 ++++++++++++-
         M src/cmd/acme/time.c                 |       1 +
         M src/cmd/acme/util.c                 |       1 +
         M src/cmd/acme/wind.c                 |       1 +
         M src/cmd/acme/xfid.c                 |       1 +
       
       23 files changed, 79 insertions(+), 13 deletions(-)
       ---
   DIR diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
                /* for generating syms in mkfile only: */
   DIR diff --git a/src/cmd/acme/addr.c b/src/cmd/acme/addr.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/buff.c b/src/cmd/acme/buff.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
       t@@ -231,7 +232,7 @@ bufloader(void *v, uint q0, Rune *r, int nr)
        }
        
        uint
       -loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg)
       +loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg, DigestState *h)
        {
                char *p;
                Rune *r;
       t@@ -253,6 +254,8 @@ loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *ar
                                warning(nil, "read error in Buffer.load");
                                break;
                        }
       +                if(h != nil)
       +                        sha1((uchar*)p+m, n, nil, h);
                        m += n;
                        p[m] = 0;
                        l = m;
       t@@ -269,11 +272,11 @@ loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *ar
        }
        
        uint
       -bufload(Buffer *b, uint q0, int fd, int *nulls)
       +bufload(Buffer *b, uint q0, int fd, int *nulls, DigestState *h)
        {
                if(q0 > b->nc)
                        error("internal error: bufload");
       -        return loadfile(fd, q0, nulls, bufloader, b);
       +        return loadfile(fd, q0, nulls, bufloader, b, h);
        }
        
        void
   DIR diff --git a/src/cmd/acme/cols.c b/src/cmd/acme/cols.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
       t@@ -105,7 +105,7 @@ struct Buffer
        };
        void                bufinsert(Buffer*, uint, Rune*, uint);
        void                bufdelete(Buffer*, uint, uint);
       -uint                bufload(Buffer*, uint, int, int*);
       +uint                bufload(Buffer*, uint, int, int*, DigestState*);
        void                bufread(Buffer*, uint, Rune*, uint);
        void                bufclose(Buffer*);
        void                bufreset(Buffer*);
       t@@ -135,8 +135,9 @@ struct File
                Rune                *name;        /* name of associated file */
                int                nname;        /* size of name */
                uvlong        qidpath;        /* of file when read */
       -        ulong                mtime;        /* of file when read */
       +        ulong        mtime;        /* of file when read */
                int                dev;                /* of file when read */
       +        uchar        sha1[20];        /* of file when read */
                int                unread;        /* file has not been read from disk */
                int                editclean;        /* mark clean after edit command */
        
       t@@ -152,7 +153,7 @@ void                fileclose(File*);
        void                filedelete(File*, uint, uint);
        void                filedeltext(File*, Text*);
        void                fileinsert(File*, uint, Rune*, uint);
       -uint                fileload(File*, uint, int, int*);
       +uint                fileload(File*, uint, int, int*, DigestState*);
        void                filemark(File*);
        void                filereset(File*);
        void                filesetname(File*, Rune*, int);
   DIR diff --git a/src/cmd/acme/disk.c b/src/cmd/acme/disk.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/ecmd.c b/src/cmd/acme/ecmd.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "edit.h"
        #include "fns.h"
       t@@ -336,7 +337,7 @@ e_cmd(Text *t, Cmd *cp)
                }
                elogdelete(f, q0, q1);
                nulls = 0;
       -        loadfile(fd, q1, &nulls, readloader, f);
       +        loadfile(fd, q1, &nulls, readloader, f, nil);
                free(s);
                close(fd);
                if(nulls)
   DIR diff --git a/src/cmd/acme/edit.c b/src/cmd/acme/edit.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "edit.h"
        #include "fns.h"
   DIR diff --git a/src/cmd/acme/elog.c b/src/cmd/acme/elog.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        #include "edit.h"
   DIR diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c
       t@@ -9,6 +9,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include <9pclient.h>
        #include "dat.h"
        #include "fns.h"
       t@@ -635,6 +636,30 @@ get(Text *et, Text *t, Text *argt, int flag1, int _0, Rune *arg, int narg)
                xfidlog(w, "get");
        }
        
       +static void
       +checksha1(char *name, File *f, Dir *d)
       +{
       +        int fd, n;
       +        DigestState *h;
       +        uchar out[20];
       +        uchar *buf;
       +        
       +        fd = open(name, OREAD);
       +        if(fd < 0)
       +                return;
       +        h = sha1(nil, 0, nil, nil);
       +        buf = emalloc(8192);
       +        while((n = read(fd, buf, 8192)) > 0)
       +                sha1(buf, n, nil, h);
       +        close(fd);
       +        sha1(nil, 0, out, h);
       +        if(memcmp(out, f->sha1, sizeof out) == 0) {
       +                f->dev = d->dev;
       +                f->qidpath = d->qid.path;
       +                f->mtime = d->mtime;
       +        }
       +}        
       +
        void
        putfile(File *f, int q0, int q1, Rune *namer, int nname)
        {
       t@@ -646,13 +671,15 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
                Dir *d, *d1;
                Window *w;
                int isapp;
       +        DigestState *h;
        
                w = f->curtext->w;
                name = runetobyte(namer, nname);
                d = dirstat(name);
                if(d!=nil && runeeq(namer, nname, f->name, f->nname)){
       -                /* f->mtime+1 because when talking over NFS it's often off by a second */
       -                if(f->dev!=d->dev || f->qidpath!=d->qid.path || labs((long)(f->mtime-d->mtime)) > 1){
       +                if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime)
       +                        checksha1(name, f, d);
       +                if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime) {
                                if(f->unread)
                                        warning(nil, "%s not written; file already exists\n", name);
                                else
       t@@ -679,6 +706,7 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
                s = fbufalloc();
                free(d);
                d = dirfstat(fd);
       +        h = sha1(nil, 0, nil, nil);
                isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND));
                if(isapp){
                        warning(nil, "%s not written; file is append only\n", name);
       t@@ -691,6 +719,7 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
                                n = BUFSIZE/UTFmax;
                        bufread(&f->b, q, r, n);
                        m = snprint(s, BUFSIZE+1, "%.*S", n, r);
       +                sha1((uchar*)s, m, nil, h);
                        if(Bwrite(b, s, m) != m){
                                warning(nil, "can't write file %s: %r\n", name);
                                goto Rescue2;
       t@@ -730,6 +759,8 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
                                f->qidpath = d->qid.path;
                                f->dev = d->dev;
                                f->mtime = d->mtime;
       +                        sha1(nil, 0, f->sha1, h);
       +                        h = nil;
                                f->mod = FALSE;
                                w->dirty = FALSE;
                                f->unread = FALSE;
       t@@ -741,6 +772,7 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
                }
                fbuffree(s);
                fbuffree(r);
       +        free(h);
                free(d);
                free(namer);
                free(name);
       t@@ -753,6 +785,7 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
                        Bterm(b);
                        free(b);
                }
       +        free(h);
                fbuffree(s);
                fbuffree(r);
                close(fd);
   DIR diff --git a/src/cmd/acme/file.c b/src/cmd/acme/file.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
       t@@ -163,11 +164,11 @@ fileunsetname(File *f, Buffer *delta)
        }
        
        uint
       -fileload(File *f, uint p0, int fd, int *nulls)
       +fileload(File *f, uint p0, int fd, int *nulls, DigestState *h)
        {
                if(f->seq > 0)
                        error("undo in file.load unimplemented");
       -        return bufload(&f->b, p0, fd, nulls);
       +        return bufload(&f->b, p0, fd, nulls, h);
        }
        
        /* return sequence number of pending redo */
   DIR diff --git a/src/cmd/acme/fns.h b/src/cmd/acme/fns.h
       t@@ -25,7 +25,7 @@ void        savemouse(Window*);
        int        restoremouse(Window*);
        void        clearmouse(void);
        void        allwindows(void(*)(Window*, void*), void*);
       -uint loadfile(int, uint, int*, int(*)(void*, uint, Rune*, int), void*);
       +uint loadfile(int, uint, int*, int(*)(void*, uint, Rune*, int), void*, DigestState*);
        void        movetodel(Window*);
        
        Window*        errorwin(Mntdir*, int);
   DIR diff --git a/src/cmd/acme/fsys.c b/src/cmd/acme/fsys.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/logf.c b/src/cmd/acme/logf.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c
       t@@ -10,6 +10,7 @@
        #include <regexp.h>
        #include <9pclient.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/regx.c b/src/cmd/acme/regx.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/rows.c b/src/cmd/acme/rows.c
       t@@ -9,6 +9,7 @@
        #include <fcall.h>
        #include <bio.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/scrl.c b/src/cmd/acme/scrl.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/text.c b/src/cmd/acme/text.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include <complete.h>
        #include "dat.h"
        #include "fns.h"
       t@@ -198,6 +199,7 @@ textload(Text *t, uint q0, char *file, int setqid)
                Dir *d, *dbuf;
                char *tmp;
                Text *u;
       +        DigestState *h;
        
                if(t->ncache!=0 || t->file->b.nc || t->w==nil || t!=&t->w->body)
                        error("text.load");
       t@@ -220,6 +222,7 @@ textload(Text *t, uint q0, char *file, int setqid)
                        goto Rescue;
                }
                nulls = FALSE;
       +        h = nil;
                if(d->qid.type & QTDIR){
                        /* this is checked in get() but it's possible the file changed underfoot */
                        if(t->file->ntext > 1){
       t@@ -264,9 +267,17 @@ textload(Text *t, uint q0, char *file, int setqid)
                }else{
                        t->w->isdir = FALSE;
                        t->w->filemenu = TRUE;
       -                q1 = q0 + fileload(t->file, q0, fd, &nulls);
       +                if(q0 == 0)
       +                        h = sha1(nil, 0, nil, nil);
       +                q1 = q0 + fileload(t->file, q0, fd, &nulls, h);
                }
                if(setqid){
       +                if(h != nil) {
       +                        sha1(nil, 0, t->file->sha1, h);
       +                        h = nil;
       +                } else {
       +                        memset(t->file->sha1, 0, sizeof t->file->sha1);
       +                }
                        t->file->dev = d->dev;
                        t->file->mtime = d->mtime;
                        t->file->qidpath = d->qid.path;
   DIR diff --git a/src/cmd/acme/time.c b/src/cmd/acme/time.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/util.c b/src/cmd/acme/util.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/wind.c b/src/cmd/acme/wind.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"
        
   DIR diff --git a/src/cmd/acme/xfid.c b/src/cmd/acme/xfid.c
       t@@ -8,6 +8,7 @@
        #include <frame.h>
        #include <fcall.h>
        #include <plumb.h>
       +#include <libsec.h>
        #include "dat.h"
        #include "fns.h"