diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/hist.c | 109 | ||||
-rw-r--r-- | Src/options.c | 1 | ||||
-rw-r--r-- | Src/zsh.h | 1 |
3 files changed, 106 insertions, 5 deletions
diff --git a/Src/hist.c b/Src/hist.c index 58a8c61ff..6deb009d5 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -2021,6 +2021,20 @@ readhistfile(char *fn, int err, int readflags) else if (!lockhistfile(fn, 1)) return; if ((in = fopen(unmeta(fn), "r"))) { +#ifdef HAVE_FCNTL_H + if (isset(HISTFCNTLLOCK)) { + struct flock lck; + + lck.l_type = F_RDLCK; + lck.l_whence = SEEK_SET; + lck.l_start = 0; + lck.l_len = 0; /* lock the whole file */ + if (fcntl(fileno(in), F_SETLKW, &lck) == -1) { + fclose(in); + return; + } + } +#endif nwordlist = 64; wordlist = (short *)zalloc(nwordlist*sizeof(short)); bufsiz = 1024; @@ -2149,6 +2163,68 @@ readhistfile(char *fn, int err, int readflags) unlockhistfile(fn); } +#ifdef HAVE_FCNTL_H +/**/ +static int +wlockfile(int fd) +{ + struct flock lck; + int ctr = 8; + + lck.l_type = F_WRLCK; + lck.l_whence = SEEK_SET; + lck.l_start = 0; + lck.l_len = 0; + while (fcntl(fd, F_SETLKW, &lck) == -1) { + if (--ctr < 0) + return 1; + sleep (1); + } + return 0; +} +#endif + +/**/ +static int +safe_unlink(const char *pathname) +{ +#ifdef HAVE_FCNTL_H + if (isset(HISTFCNTLLOCK)) { + int fd = open(pathname, O_WRONLY | O_NOCTTY, 0600); + if (fd >= 0) { + int err = wlockfile(fd) || unlink(pathname); + close(fd); + return err; + } else { + return errno != ENOENT; + } + } +#endif + return unlink(pathname) && errno != ENOENT; +} + +/**/ +static int +safe_rename(const char *oldpath, const char *newpath) +{ +#ifdef HAVE_FCNTL_H + if (isset(HISTFCNTLLOCK)) { + int fd = open(newpath, O_CREAT | O_WRONLY | O_NOCTTY, 0600); + if (fd < 0) { + return 1; + } else if (wlockfile(fd)) { + close(fd); + return 1; + } else { + int err = rename(oldpath, newpath); + close(fd); + return err; + } + } +#endif + return rename(oldpath, newpath); +} + /**/ void savehistfile(char *fn, int err, int writeflags) @@ -2158,6 +2234,9 @@ savehistfile(char *fn, int err, int writeflags) Histent he; zlong xcurhist = curhist - !!(histactive & HA_ACTIVE); int extended_history = isset(EXTENDEDHISTORY); +#ifdef HAVE_FTRUNCATE + int truncate_history = 0; +#endif int ret; if (!interact || savehistsiz <= 0 || !hist_ring @@ -2196,12 +2275,16 @@ savehistfile(char *fn, int err, int writeflags) tmpfile = NULL; out = fd >= 0 ? fdopen(fd, "a") : NULL; } else if (!isset(HISTSAVEBYCOPY)) { - int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600); + int fd = open(unmeta(fn), O_CREAT | O_WRONLY | O_NOCTTY, 0600); tmpfile = NULL; out = fd >= 0 ? fdopen(fd, "w") : NULL; +#ifdef HAVE_FTRUNCATE + /* The file should be truncated after its locking. */ + truncate_history = 1; +#endif } else { tmpfile = bicat(unmeta(fn), ".new"); - if (unlink(tmpfile) < 0 && errno != ENOENT) + if (safe_unlink(tmpfile)) out = NULL; else { struct stat sb; @@ -2239,7 +2322,18 @@ savehistfile(char *fn, int err, int writeflags) #endif } } +#ifdef HAVE_FCNTL_H + if (out && isset(HISTFCNTLLOCK) && wlockfile(fileno(out))) { + zerr("can't lock file (timeout) -- history %s not updated", fn); + err = 0; /* Don't report a generic error below. */ + out = NULL; + } +#endif if (out) { +#ifdef HAVE_FTRUNCATE + if (truncate_history) + ftruncate(fileno(out), 0); +#endif ret = 0; for (; he && he->histnum <= xcurhist; he = down_histent(he)) { if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP) @@ -2285,16 +2379,19 @@ savehistfile(char *fn, int err, int writeflags) zsfree(lasthist.text); lasthist.text = ztrdup(start); } - } - if (fclose(out) < 0 && ret >= 0) + } else if (ret >= 0 && fflush(out) < 0) { ret = -1; + } if (ret >= 0) { if (tmpfile) { - if (rename(tmpfile, unmeta(fn)) < 0) + /* out has been flushed and the file must be renamed while + being open so that the lock is still valid */ + if (safe_rename(tmpfile, unmeta(fn))) zerr("can't rename %s.new to $HISTFILE", fn); free(tmpfile); tmpfile = NULL; } + fclose(out); if (writeflags & HFILE_SKIPOLD && !(writeflags & (HFILE_FAST | HFILE_NO_REWRITE))) { @@ -2314,6 +2411,8 @@ savehistfile(char *fn, int err, int writeflags) pophiststack(); histactive = remember_histactive; } + } else { + fclose(out); } } else ret = -1; diff --git a/Src/options.c b/Src/options.c index a206b2910..9fb3f3388 100644 --- a/Src/options.c +++ b/Src/options.c @@ -135,6 +135,7 @@ static struct optname optns[] = { {{NULL, "histallowclobber", 0}, HISTALLOWCLOBBER}, {{NULL, "histbeep", OPT_ALL}, HISTBEEP}, {{NULL, "histexpiredupsfirst",0}, HISTEXPIREDUPSFIRST}, +{{NULL, "histfcntllock", 0}, HISTFCNTLLOCK}, {{NULL, "histfindnodups", 0}, HISTFINDNODUPS}, {{NULL, "histignorealldups", 0}, HISTIGNOREALLDUPS}, {{NULL, "histignoredups", 0}, HISTIGNOREDUPS}, diff --git a/Src/zsh.h b/Src/zsh.h index 2b8646cb1..67e4c0c31 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1749,6 +1749,7 @@ enum { HISTALLOWCLOBBER, HISTBEEP, HISTEXPIREDUPSFIRST, + HISTFCNTLLOCK, HISTFINDNODUPS, HISTIGNOREALLDUPS, HISTIGNOREDUPS, |