tcsh/tcsh-6.21.0-history-file-locking.patch
2019-08-22 13:04:42 +00:00

693 lines
19 KiB
Diff

Based on f813180f2fc1d682dd097e4a05ef4d15000204ad Mon Sep 17 00:00:00 2001
From: Roman Kollar <rkollar@redhat.com>
Date: Mon, 29 Oct 2012 17:52:52 +0100
Subject: [PATCH] Add .history file locking - shared readers, exclusive writer
Originally reported at Red Hat Bugzilla:
https://bugzilla.redhat.com/show_bug.cgi?id=648592
Patch by Vojtech Vitek (V-Teq) <vvitek@redhat.com>
Additional changes reflecting:
https://bugzilla.redhat.com/show_bug.cgi?id=879371
Changes by Fridolin Pokorny <fpokorny@redhat.com>
---
sh.c | 102 ++++++++++++++++++++++++++++++++++++------------
sh.decls.h | 4 +
sh.dol.c | 2
sh.err.c | 16 +++++++
sh.h | 18 ++++++++
sh.hist.c | 127 ++++++++++++++++++++++++++++---------------------------------
sh.lex.c | 8 +--
sh.sem.c | 2
8 files changed, 178 insertions(+), 101 deletions(-)
--- sh.c
+++ sh.c 2019-08-22 13:02:31.780361102 +0000
@@ -138,6 +138,7 @@ struct saved_state {
int cantell;
struct Bin B;
int justpr;
+ int close_unit;
};
static int srccat (Char *, Char *);
@@ -1359,7 +1360,7 @@ main(int argc, char **argv)
/*
* Source history before .login so that it is available in .login
*/
- loadhist(NULL, 0);
+ loadhist(NULL, HIST_FILE_RDLCK);
#ifndef LOGINFIRST
if (loginsh)
(void) srccat(varval(STRhome), STRsldotlogin);
@@ -1508,7 +1509,7 @@ static int
srccat(Char *cp, Char *dp)
{
if (cp[0] == '/' && cp[1] == '\0')
- return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL);
+ return srcfile(short2str(dp), (mflag ? 0 : HIST_ONLY), 0, NULL);
else {
Char *ep;
char *ptr;
@@ -1524,7 +1525,7 @@ srccat(Char *cp, Char *dp)
cleanup_push(ep, xfree);
ptr = short2str(ep);
- rv = srcfile(ptr, (mflag ? 0 : 1), 0, NULL);
+ rv = srcfile(ptr, (mflag ? 0 : HIST_ONLY), 0, NULL);
cleanup_until(ep);
return rv;
}
@@ -1538,20 +1539,49 @@ static int
#else
int
#endif /*WINNT_NATIVE*/
-srcfile(const char *f, int onlyown, int flag, Char **av)
+srcfile(const char *f, int onlyown, int flg, Char **av)
{
- int unit;
+ int *unit;
- if ((unit = xopen(f, O_RDONLY|O_LARGEFILE)) == -1)
- return 0;
- cleanup_push(&unit, open_cleanup);
- unit = dmove(unit, -1);
- cleanup_ignore(&unit);
- cleanup_until(&unit);
-
- (void) close_on_exec(unit, 1);
- srcunit(unit, onlyown, flag, av);
- return 1;
+ unit = xmalloc(sizeof(*unit));
+ cleanup_push(unit, xfree);
+ *unit = xopen(f, O_LARGEFILE |
+ ((flg & HIST_FILE_WRLCK) ? (O_CREAT|O_RDWR) : O_RDONLY), 0600);
+ if (*unit == -1)
+ return 0; /* Error. */
+
+ cleanup_push(unit, open_cleanup);
+ *unit = dmove(*unit, -1);
+ (void) close_on_exec(*unit, 1);
+
+ if (flg & (HIST_FILE_WRLCK | HIST_FILE_RDLCK)) {
+ struct flock fl;
+
+ fl.l_type = (flg & HIST_FILE_WRLCK) ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ cleanup_push(unit, fcntl_cleanup);
+ if (fcntl(*unit, F_SETLKW, &fl) == -1)
+ cleanup_ignore(unit);
+ }
+
+ srcunit(*unit, onlyown, flg, av);
+
+ /* Unlock the unit, if we don't want to leave it locked (or open). */
+ if ((flg & (HIST_FILE_WRLCK | HIST_FILE_RDLCK)) &&
+ (!(flg & HIST_FILE_LOCK) || !(flg & HIST_FILE_OPEN)))
+ cleanup_until(unit); /* fcntl_cleanup */
+
+ /* Close the unit, if we don't want to leave it open. */
+ if (!(flg & HIST_FILE_OPEN)) {
+ cleanup_until(unit); /* open_cleanup */
+ cleanup_until(unit); /* xfree */
+ return -1; /* Not error but invalid file descriptor. */
+ }
+
+ return *unit; /* File descriptor (fd > FSAFE). */
}
@@ -1560,7 +1590,7 @@ srcfile(const char *f, int onlyown, int
* fd.
*/
static void
-st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
+st_save(struct saved_state *st, int unit, int flg, Char **al, Char **av)
{
st->insource = insource;
st->SHIN = SHIN;
@@ -1609,10 +1639,14 @@ st_save(struct saved_state *st, int unit
st->onelflg = onelflg;
st->enterhist = enterhist;
st->justpr = justpr;
- if (hflg)
+ if (flg & (HIST_ONLY | HIST_MERGE))
st->HIST = HIST;
else
st->HIST = '\0';
+ if (flg & HIST_FILE_OPEN)
+ st->close_unit = 0;
+ else
+ st->close_unit = 1;
st->cantell = cantell;
cpybin(st->B, B);
@@ -1651,7 +1685,7 @@ st_save(struct saved_state *st, int unit
evalp = 0;
alvec = al;
alvecp = 0;
- enterhist = hflg;
+ enterhist = flg & (HIST_ONLY | HIST_MERGE);
if (enterhist)
HIST = '\0';
insource = 1;
@@ -1684,7 +1718,8 @@ st_restore(void *xst)
}
cpybin(B, st->B);
- xclose(SHIN);
+ if (st->close_unit)
+ xclose(SHIN);
insource = st->insource;
SHIN = st->SHIN;
@@ -1720,7 +1755,7 @@ st_restore(void *xst)
* we don't chance it. This occurs on ".cshrc"s and the like.
*/
static void
-srcunit(int unit, int onlyown, int hflg, Char **av)
+srcunit(int unit, int onlyown, int flg, Char **av)
{
struct saved_state st;
@@ -1746,7 +1781,7 @@ srcunit(int unit, int onlyown, int hflg,
}
/* Save the current state and move us to a new state */
- st_save(&st, unit, hflg, NULL, av);
+ st_save(&st, unit, flg, NULL, av);
/*
* Now if we are allowing commands to be interrupted, we let ourselves be
@@ -2088,7 +2123,7 @@ process(int catch)
* elsewhere...
*/
if (enterhist || (catch && intty && !whyles && !tellwhat && !arun))
- savehist(&paraml, enterhist > 1);
+ savehist(&paraml, enterhist > 1 ? HIST_MERGE : 0);
if (Expand && seterr)
Expand = 0;
@@ -2175,21 +2210,28 @@ process(int catch)
void
dosource(Char **t, struct command *c)
{
+ (void) dosource_flg(t, c, 0);
+}
+
+int
+dosource_flg(Char **t, struct command *c, int flg)
+{
Char *f;
- int hflg = 0;
char *file;
+ int fd;
+ int newflg = 0;
USE(c);
t++;
if (*t && eq(*t, STRmh)) {
if (*++t == NULL)
stderror(ERR_NAME | ERR_HFLAG);
- hflg++;
+ newflg |= HIST_ONLY;
}
else if (*t && eq(*t, STRmm)) {
if (*++t == NULL)
stderror(ERR_NAME | ERR_MFLAG);
- hflg = 2;
+ newflg |= HIST_MERGE;
}
f = globone(*t++, G_ERROR);
@@ -2198,9 +2240,15 @@ dosource(Char **t, struct command *c)
xfree(f);
t = glob_all_or_error(t);
cleanup_push(t, blk_cleanup);
- if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
+ fd = srcfile(file, 0, (flg | newflg), t);
+ if ((!fd) && (!newflg) && (!bequiet))
stderror(ERR_SYSTEM, file, strerror(errno));
- cleanup_until(file);
+ /* We need to preserve fd and it's cleaning routines on the top of the
+ * cleaning stack. Don't call cleanup_until() but clean it manually. */
+ cleanup_ignore(file);
+ xfree(file);
+
+ return fd; /* Valid/invalid file descriptor (>FSAVE, -1). Zero on error. */
}
/*
--- sh.decls.h
+++ sh.decls.h 2019-08-22 13:02:31.780361102 +0000
@@ -37,6 +37,7 @@
*/
extern Char *gethdir (const Char *);
extern void dosource (Char **, struct command *);
+extern int dosource_flg (Char **, struct command *, int);
extern void exitstat (void);
extern void goodbye (Char **, struct command *);
extern void importpath (Char *);
@@ -98,6 +99,7 @@ extern void cleanup_until_mark(void);
extern size_t cleanup_push_mark(void);
extern void cleanup_pop_mark(size_t);
extern void open_cleanup(void *);
+extern void fcntl_cleanup(void *);
extern void opendir_cleanup(void *);
extern void sigint_cleanup(void *);
extern void sigprocmask_cleanup(void *);
@@ -220,7 +222,7 @@ extern struct Hist *enthist (int, str
extern void savehist (struct wordent *, int);
extern char *fmthist (int, ptr_t);
extern void rechist (Char *, int);
-extern void loadhist (Char *, int);
+extern int loadhist (Char *, int);
extern void displayHistStats(const char *);
extern void sethistory (int);
--- sh.dol.c
+++ sh.dol.c 2019-08-22 13:02:31.780361102 +0000
@@ -1117,6 +1117,6 @@ again:
*obp = 0;
tmp = short2str(obuf);
(void) xwrite(0, tmp, strlen (tmp));
- (void) lseek(0, (off_t) 0, L_SET);
+ (void) lseek(0, (off_t) 0, SEEK_SET);
cleanup_until(&inheredoc);
}
--- sh.err.c
+++ sh.err.c 2019-08-22 13:02:31.780361102 +0000
@@ -511,6 +511,22 @@ open_cleanup(void *xptr)
}
void
+fcntl_cleanup(void *xptr)
+{
+ int *ptr;
+ struct flock fl;
+
+ ptr = xptr;
+
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ fcntl(*ptr, F_SETLK, &fl);
+}
+
+void
opendir_cleanup(void *xdir)
{
DIR *dir;
--- sh.h
+++ sh.h 2019-08-22 13:02:31.784361027 +0000
@@ -49,6 +49,24 @@
# include <inttypes.h>
#endif
+#include <unistd.h>
+#include <fcntl.h>
+
+/*
+ * History flags.
+ */
+#define HIST_ONLY 0x001
+#define HIST_SAVE 0x002
+#define HIST_LOAD 0x004
+#define HIST_REV 0x008
+#define HIST_CLEAR 0x010
+#define HIST_MERGE 0x020
+#define HIST_TIME 0x040
+#define HIST_FILE_WRLCK 0x080 /* Write lock */
+#define HIST_FILE_RDLCK 0x100 /* Read lock */
+#define HIST_FILE_OPEN 0x200 /* Leave file open */
+#define HIST_FILE_LOCK 0x400 /* Leave file locked */
+
#if !defined(HAVE_STDINT_H) && !defined(HAVE_INTTYPES_H) && !defined(WINNT_NATIVE)
typedef unsigned long intptr_t;
#endif
--- sh.hist.c
+++ sh.hist.c 2019-08-22 13:02:44.684119766 +0000
@@ -32,6 +32,8 @@
#include "sh.h"
#include <stdio.h> /* for rename(2), grr. */
#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
#include "tc.h"
#include "dotlock.h"
@@ -42,14 +44,6 @@ Char HistLit = 0;
static int heq (const struct wordent *, const struct wordent *);
static void hfree (struct Hist *);
-#define HIST_ONLY 0x01
-#define HIST_SAVE 0x02
-#define HIST_LOAD 0x04
-#define HIST_REV 0x08
-#define HIST_CLEAR 0x10
-#define HIST_MERGE 0x20
-#define HIST_TIME 0x40
-
/*
* C shell
*/
@@ -141,13 +135,13 @@ discardExcess(int hlen)
void
savehist(
struct wordent *sp,
- int mflg) /* true if -m (merge) specified */
+ int flg) /* true if -m (merge) specified */
{
/* throw away null lines */
if (sp && sp->next->word[0] == '\n')
return;
if (sp)
- (void) enthist(++eventno, sp, 1, mflg, histlen);
+ (void) enthist(++eventno, sp, 1, flg, histlen);
discardExcess(histlen);
}
@@ -919,7 +913,7 @@ enthist(
int event, /* newly incremented global eventno */
struct wordent *lp,
int docopy,
- int mflg, /* true if merge requested */
+ int flg, /* true if merge requested */
int hlen) /* -1 if unknown */
{
struct Hist *p = NULL, *pp = &Histlist, *pTime = NULL;
@@ -939,7 +933,7 @@ enthist(
Htime = p->Htime;
/* If we are merging, and the old entry is at the place we want
* to insert the new entry, then remember the place. */
- if (mflg && Htime != 0 && p->Hprev->Htime >= Htime)
+ if ((flg & HIST_MERGE) && Htime != 0 && p->Hprev->Htime >= Htime)
pTime = p->Hprev;
if (!fastMergeErase)
renumberHist(p); /* Reset Href of subsequent entries */
@@ -998,7 +992,7 @@ enthist(
/* The head of history list is the default insertion point.
If merging, advance insertion point, in pp, according to Htime. */
/* XXX -- In histdup=all, Htime values can be non-monotonic. */
- if (mflg) { /* merge according to np->Htime */
+ if (flg & HIST_MERGE) { /* merge according to np->Htime */
pp = mergeInsertionPoint(np, pTime);
for (p = pp->Hnext; p && p->Htime == np->Htime; pp = p, p = p->Hnext) {
if (heq(&p->Hlex, &np->Hlex)) {
@@ -1037,11 +1031,11 @@ hfree(struct Hist *hp)
}
PG_STATIC void
-phist(struct Hist *hp, int hflg)
+phist(struct Hist *hp, int flg)
{
if (hp->Href < 0)
return;
- if (hflg & HIST_ONLY) {
+ if (flg & HIST_ONLY) {
int old_output_raw;
/*
@@ -1053,7 +1047,7 @@ phist(struct Hist *hp, int hflg)
old_output_raw = output_raw;
output_raw = 1;
cleanup_push(&old_output_raw, output_raw_restore);
- if (hflg & HIST_TIME)
+ if (flg & HIST_TIME)
/*
* Make file entry with history time in format:
* "+NNNNNNNNNN" (10 digits, left padded with ascii '0')
@@ -1084,7 +1078,7 @@ phist(struct Hist *hp, int hflg)
}
PG_STATIC void
-dophist(int n, int hflg)
+dophist(int n, int flg)
{
struct Hist *hp;
if (setintr) {
@@ -1093,7 +1087,7 @@ dophist(int n, int hflg)
pintr_push_enable(&old_pintr_disabled);
cleanup_until(&old_pintr_disabled);
}
- if ((hflg & HIST_REV) == 0) {
+ if (!(flg & HIST_REV)) {
/* Since the history list is stored most recent first, non-reversing
* print needs to print (backwards) up the list. */
if ((unsigned)n >= histCount)
@@ -1107,10 +1101,10 @@ dophist(int n, int hflg)
if (hp == NULL)
return; /* nothing to print */
for (; hp != &Histlist; hp = hp->Hprev)
- phist(hp, hflg);
+ phist(hp, flg);
} else {
for (hp = Histlist.Hnext; n-- > 0 && hp != NULL; hp = hp->Hnext)
- phist(hp, hflg);
+ phist(hp, flg);
}
}
@@ -1118,7 +1112,7 @@ dophist(int n, int hflg)
void
dohist(Char **vp, struct command *c)
{
- int n, hflg = 0;
+ int n, flg = 0;
USE(c);
if (getn(varval(STRhistory)) == 0)
@@ -1129,40 +1123,40 @@ dohist(Char **vp, struct command *c)
while (*++vp2)
switch (*vp2) {
case 'c':
- hflg |= HIST_CLEAR;
+ flg |= HIST_CLEAR;
break;
case 'h':
- hflg |= HIST_ONLY;
+ flg |= HIST_ONLY;
break;
case 'r':
- hflg |= HIST_REV;
+ flg |= HIST_REV;
break;
case 'S':
- hflg |= HIST_SAVE;
+ flg |= HIST_SAVE;
break;
case 'L':
- hflg |= HIST_LOAD;
+ flg |= HIST_LOAD;
break;
case 'M':
- hflg |= HIST_MERGE;
+ flg |= HIST_MERGE;
break;
case 'T':
- hflg |= HIST_TIME;
+ flg |= HIST_TIME;
break;
default:
stderror(ERR_HISTUS, "chrSLMT");
break;
}
}
- if (hflg & HIST_CLEAR) {
+ if (flg & HIST_CLEAR) {
struct Hist *np, *hp;
for (hp = &Histlist; (np = hp->Hnext) != NULL;)
hremove(np), hfree(np);
}
- if (hflg & (HIST_LOAD | HIST_MERGE))
- loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0);
- else if (hflg & HIST_SAVE)
+ if (flg & (HIST_LOAD | HIST_MERGE))
+ loadhist(*vp, (flg | HIST_FILE_RDLCK));
+ else if (flg & HIST_SAVE)
rechist(*vp, 1);
else {
if (*vp)
@@ -1170,7 +1164,7 @@ dohist(Char **vp, struct command *c)
else {
n = getn(varval(STRhistory));
}
- dophist(n, hflg);
+ dophist(n, flg);
}
}
@@ -1218,10 +1212,9 @@ dotlock_cleanup(void* lockpath)
void
rechist(Char *fname, int ref)
{
- Char *snum, *rs;
- int fp, ftmp, oldidfds, phup_disabled_tmp;
+ Char *snum;
+ int fd = -1, ftmp, oldidfds, phup_disabled_tmp;
struct varent *shist;
- char path[MAXPATHLEN];
struct stat st;
static Char *dumphist[] = {STRhistory, STRmhT, 0, 0};
@@ -1294,52 +1287,50 @@ rechist(Char *fname, int ref)
cleanup_push(lockpath, dotlock_cleanup);
#endif
}
- loadhist(fname, 1);
+ /* Read .history file, leave it's fd open for writing. */
+ fd = loadhist(fname, HIST_MERGE|HIST_FILE_WRLCK|HIST_FILE_OPEN|HIST_FILE_LOCK);
+ if (fd > 0) {
+ /* Truncate the .history file. */
+ (void) ftruncate(fd, 0);
+ (void) lseek(fd, (off_t) 0, SEEK_SET);
+ }
}
}
- rs = randsuf();
- xsnprintf(path, sizeof(path), "%S.%S", fname, rs);
- xfree(rs);
-
- fp = xcreat(path, 0600);
- if (fp == -1) {
- didfds = oldidfds;
- cleanup_until(fname);
- phup_disabled = phup_disabled_tmp;
- return;
+ if (fd <= 0) {
+ /* Open .history file for writing (if not open yet). */
+ fd = xopen(short2str(fname), O_LARGEFILE|O_CREAT|O_WRONLY|O_TRUNC, 0600);
+ if (fd != -1)
+ cleanup_push(&fd, open_cleanup);
}
/* Try to preserve ownership and permissions of the original history file */
#ifndef WINNT_NATIVE
if (stat(short2str(fname), &st) != -1) {
- TCSH_IGNORE(fchown(fp, st.st_uid, st.st_gid));
- TCSH_IGNORE(fchmod(fp, st.st_mode));
+ TCSH_IGNORE(fchown(fd, st.st_uid, st.st_gid));
+ TCSH_IGNORE(fchmod(fd, st.st_mode));
}
#else
UNREFERENCED_PARAMETER(st);
#endif
- ftmp = SHOUT;
- SHOUT = fp;
- dumphist[2] = snum;
- dohist(dumphist, NULL);
- xclose(fp);
- SHOUT = ftmp;
+ if (fd != -1) {
+ ftmp = SHOUT;
+ SHOUT = fd;
+ dumphist[2] = snum;
+ dohist(dumphist, NULL);
+ SHOUT = ftmp;
+ }
didfds = oldidfds;
-#ifndef WINNT_NATIVE
- (void)rename(path, short2str(fname));
-#else
- (void)ReplaceFile( short2str(fname),path,NULL,0,NULL,NULL);
-#endif
cleanup_until(fname);
phup_disabled = phup_disabled_tmp;
}
/* This is the entry point for loading history data from a file. */
-void
-loadhist(Char *fname, int mflg)
+int
+loadhist(Char *fname, int flg)
{
static Char *loadhist_cmd[] = {STRsource, NULL, NULL, NULL};
- loadhist_cmd[1] = mflg ? STRmm : STRmh;
+ int fd;
+ loadhist_cmd[1] = (flg & HIST_MERGE) ? STRmm : STRmh;
if (fname != NULL)
loadhist_cmd[2] = fname;
@@ -1348,17 +1339,19 @@ loadhist(Char *fname, int mflg)
else
loadhist_cmd[2] = STRtildothist;
- dosource(loadhist_cmd, NULL);
+ fd = dosource_flg(loadhist_cmd, NULL, flg);
- /* During history merging (enthist sees mflg set), we disable management of
- * Hnum and Href (because fastMergeErase is true). So now reset all the
+ /* During history merging (enthist sees merge flag), we disable management
+ * of Hnum and Href (because fastMergeErase is true). So now reset all the
* values based on the final ordering of the history list. */
- if (mflg) {
+ if (flg & HIST_MERGE) {
int n = eventno;
struct Hist *hp = &Histlist;
while ((hp = hp->Hnext))
hp->Hnum = hp->Href = n--;
}
+
+ return fd; /* Valid/invalid file descriptor (>FSAVE, -1). Zero on error. */
}
void
--- sh.lex.c
+++ sh.lex.c 2019-08-22 13:02:31.784361027 +0000
@@ -1608,7 +1608,7 @@ wide_read(int fildes, Char *buf, size_t
/* Throwing away possible partial multibyte characters on error if the
stream is not seekable */
err = errno;
- lseek(fildes, -(off_t)partial, L_INCR);
+ lseek(fildes, -(off_t)partial, SEEK_CUR);
errno = err;
return res != 0 ? res : r;
}
@@ -1623,7 +1623,7 @@ bgetc(void)
if (cantell) {
if (fseekp < fbobp || fseekp > feobp) {
fbobp = feobp = fseekp;
- (void) lseek(SHIN, fseekp, L_SET);
+ (void) lseek(SHIN, fseekp, SEEK_SET);
}
if (fseekp == feobp) {
#ifdef WIDE_STRINGS
@@ -1827,7 +1827,7 @@ btell(struct Ain *l)
void
btoeof(void)
{
- (void) lseek(SHIN, (off_t) 0, L_XTND);
+ (void) lseek(SHIN, (off_t) 0, SEEK_END);
aret = TCSH_F_SEEK;
fseekp = feobp;
alvec = NULL;
@@ -1845,7 +1845,7 @@ settell(void)
cantell = 0;
if (arginp || onelflg || intty)
return;
- if ((x = lseek(SHIN, (off_t) 0, L_INCR)) == -1)
+ if ((x = lseek(SHIN, (off_t) 0, SEEK_CUR)) == -1)
return;
fbuf = xcalloc(2, sizeof(Char **));
fblocks = 1;
--- sh.sem.c
+++ sh.sem.c 2019-08-22 13:02:31.784361027 +0000
@@ -905,7 +905,7 @@ doio(struct command *t, int *pipein, int
fd = xopen(tmp, O_WRONLY|O_APPEND|O_LARGEFILE);
#else /* !O_APPEND */
fd = xopen(tmp, O_WRONLY|O_LARGEFILE);
- (void) lseek(fd, (off_t) 0, L_XTND);
+ (void) lseek(fd, (off_t) 0, SEEK_END);
#endif /* O_APPEND */
}
else