summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVojtech Vitek (V-Teq) <vvitek@redhat.com>2012-02-16 15:47:11 +0100
committerVojtech Vitek (V-Teq) <vvitek@redhat.com>2012-02-16 15:47:11 +0100
commitc8ffd8642254a5ae0fd07524d88a57663417145d (patch)
treeed1779daf09adddb9224c894566e124338f28702
parent8fe060db99d7fbfdabb66df358c8920e3419c69c (diff)
downloadtcsh-rebase-6.19.00-c8ffd8642254a5ae0fd07524d88a57663417145d.tar.gz
tcsh-rebase-6.19.00-c8ffd8642254a5ae0fd07524d88a57663417145d.tar.xz
tcsh-rebase-6.19.00-c8ffd8642254a5ae0fd07524d88a57663417145d.zip
Prevent .history file corruption
- Implement file locking using shared readers, exclusive writer to prevent any .history file data corruption - Handle pending signals before flush so that the the .history file does not get truncated Resolves: #653054
-rw-r--r--tcsh-6.17.00-handle-signals-before-flush.patch36
-rw-r--r--tcsh-6.17.00-history-file-locking.patch478
-rw-r--r--tcsh.spec10
3 files changed, 524 insertions, 0 deletions
diff --git a/tcsh-6.17.00-handle-signals-before-flush.patch b/tcsh-6.17.00-handle-signals-before-flush.patch
new file mode 100644
index 0000000..2fdbfc9
--- /dev/null
+++ b/tcsh-6.17.00-handle-signals-before-flush.patch
@@ -0,0 +1,36 @@
+From 466297a90d3520d5521c9ca6858a5b7981b3edb8 Mon Sep 17 00:00:00 2001
+From: "Vojtech Vitek (V-Teq)" <vvitek@redhat.com>
+Date: Thu, 10 Nov 2011 17:33:01 +0100
+Subject: [PATCH 1/2] Handle pending signals before flush so that the the
+ history file does not get truncated. (Ted Anderson)
+
+Patch adapted from upstream patch in tcsh V6.17.01 - 20100506
+http://mx.gw.com/pipermail/tcsh/2010-May/004066.html
+---
+ sh.c | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/sh.c b/sh.c
+index 73b6d7f..da818fb 100644
+--- a/sh.c
++++ b/sh.c
+@@ -1288,6 +1288,8 @@ main(int argc, char **argv)
+ */
+ process(setintr);
+
++ /* Take care of these (especially HUP) here instead of inside flush. */
++ handle_pending_signals();
+ /*
+ * Mop-up.
+ */
+@@ -2018,6 +2020,7 @@ process(int catch)
+ cleanup_pop_mark(omark);
+ resexit(osetexit);
+ exitset--;
++ handle_pending_signals();
+ }
+
+ /*ARGSUSED*/
+--
+1.7.6.2
+
diff --git a/tcsh-6.17.00-history-file-locking.patch b/tcsh-6.17.00-history-file-locking.patch
new file mode 100644
index 0000000..c30271a
--- /dev/null
+++ b/tcsh-6.17.00-history-file-locking.patch
@@ -0,0 +1,478 @@
+From c0612dbd1c6d78668f46910c20cb801a5c67780b Mon Sep 17 00:00:00 2001
+From: "Vojtech Vitek (V-Teq)" <vvitek@redhat.com>
+Date: Thu, 10 Nov 2011 18:19:57 +0100
+Subject: [PATCH 2/2] Implement .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>
+---
+ sh.c | 91 ++++++++++++++++++++++++++++++++++++++++++++----------------
+ sh.decls.h | 5 ++-
+ sh.dir.c | 2 +-
+ sh.dol.c | 2 +-
+ sh.err.c | 16 ++++++++++
+ sh.h | 17 +++++++++++
+ sh.hist.c | 83 +++++++++++++++++++++++++++++-------------------------
+ sh.lex.c | 8 ++--
+ sh.sem.c | 2 +-
+ 9 files changed, 154 insertions(+), 72 deletions(-)
+
+diff --git a/sh.c b/sh.c
+index da818fb..283af06 100644
+--- a/sh.c
++++ b/sh.c
+@@ -140,6 +140,7 @@ struct saved_state {
+ int cantell;
+ struct Bin B;
+ int justpr;
++ int close_unit;
+ };
+
+ static int srccat (Char *, Char *);
+@@ -1251,7 +1252,7 @@ main(int argc, char **argv)
+ /*
+ * Source history before .login so that it is available in .login
+ */
+- loadhist(NULL, 0);
++ loadhist(NULL, FD_RDLCK);
+ #ifndef LOGINFIRST
+ if (loginsh)
+ (void) srccat(varval(STRhome), STRsldotlogin);
+@@ -1404,20 +1405,52 @@ 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;
+-
+- 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;
++ int *unit, copy;
++
++ unit = xmalloc(sizeof(*unit));
++ cleanup_push(unit, xfree);
++ *unit = xopen(f, O_LARGEFILE |
++ ((flg & FD_WRLCK) ? (O_CREAT|O_RDWR) : O_RDONLY), 0600);
++
++ if (*unit == -1) {
++ if (!bequiet)
++ stderror(ERR_SYSTEM, f, strerror(errno));
++ return -1;
++ }
++
++ cleanup_push(unit, open_cleanup);
++ *unit = dmove(*unit, -1);
++ (void) close_on_exec(*unit, 1);
++ copy = *unit;
++
++ if (flg & (FD_WRLCK | FD_RDLCK)) {
++ struct flock fl;
++
++ fl.l_type = (flg & FD_WRLCK) ? F_WRLCK : F_RDLCK;
++ fl.l_whence = SEEK_SET;
++ fl.l_start = 0;
++ fl.l_len = 0;
++
++ if (fcntl(*unit, F_SETLKW, &fl) != -1)
++ cleanup_push(unit, fcntl_cleanup);
++ }
++
++ srcunit(copy, onlyown, (flg & (SRC_HFLAG | SRC_MFLAG)), av);
++
++ /* Close the unit, if we don't want to leave it open & locked. */
++ if ((flg & (FD_WRLCK | FD_RDLCK)) && (!(flg & FD_LEAVE_LCKD))) {
++ cleanup_until(unit); /* fcntl_cleanup */
++ if (!(flg & FD_LEAVE_OPEN)) {
++ cleanup_until(unit); /* open_cleanup */
++ cleanup_until(unit); /* xfree */
++ }
++
++ return -1; /* Invalid file descriptor. */
++ }
++
++ return *unit;
+ }
+
+
+@@ -1475,10 +1508,14 @@ st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
+ st->onelflg = onelflg;
+ st->enterhist = enterhist;
+ st->justpr = justpr;
+- if (hflg)
++ if (hflg && SRC_HFLAG)
+ st->HIST = HIST;
+ else
+ st->HIST = '\0';
++ if (hflg && FD_LEAVE_OPEN)
++ st->close_unit = 0;
++ else
++ st->close_unit = 1;
+ st->cantell = cantell;
+ cpybin(st->B, B);
+
+@@ -1550,7 +1587,8 @@ st_restore(void *xst)
+ }
+ cpybin(B, st->B);
+
+- xclose(SHIN);
++ if (st->close_unit)
++ xclose(SHIN);
+
+ insource = st->insource;
+ SHIN = st->SHIN;
+@@ -2024,24 +2062,25 @@ process(int catch)
+ }
+
+ /*ARGSUSED*/
+-void
+-dosource(Char **t, struct command *c)
++int
++dosource(Char **t, struct command *c, int flg)
+ {
+ Char *f;
+- int hflg = 0;
+ char *file;
++ int fd;
++ int hflg = 0;
+
+ USE(c);
+ t++;
+ if (*t && eq(*t, STRmh)) {
+ if (*++t == NULL)
+ stderror(ERR_NAME | ERR_HFLAG);
+- hflg++;
++ hflg |= SRC_HFLAG;
+ }
+ else if (*t && eq(*t, STRmm)) {
+ if (*++t == NULL)
+ stderror(ERR_NAME | ERR_MFLAG);
+- hflg = 2;
++ hflg |= SRC_MFLAG;
+ }
+
+ f = globone(*t++, G_ERROR);
+@@ -2049,9 +2088,13 @@ dosource(Char **t, struct command *c)
+ cleanup_push(file, xfree);
+ xfree(f);
+ t = glob_all_or_error(t);
+- if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
+- stderror(ERR_SYSTEM, file, strerror(errno));
+- cleanup_until(file);
++ fd = srcfile(file, 0, (hflg | flg), t);
++
++ /* Postpone fd cleanup, which is on the top of cleaning stack. */
++ cleanup_ignore(file);
++ xfree(file);
++
++ return fd; /* File descriptor or -1. */
+ }
+
+ /*
+diff --git a/sh.decls.h b/sh.decls.h
+index 70f76a4..806ae3a 100644
+--- a/sh.decls.h
++++ b/sh.decls.h
+@@ -37,7 +37,7 @@
+ * sh.c
+ */
+ extern Char *gethdir (const Char *);
+-extern void dosource (Char **, struct command *);
++extern int dosource (Char **, struct command *, int);
+ extern void exitstat (void);
+ extern void goodbye (Char **, struct command *);
+ extern void importpath (Char *);
+@@ -97,6 +97,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 *);
+@@ -216,7 +217,7 @@ extern struct Hist *enthist (int, struct wordent *, int, int);
+ 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);
+
+ /*
+ * sh.init.c
+diff --git a/sh.dir.c b/sh.dir.c
+index 9f72951..b75c366 100644
+--- a/sh.dir.c
++++ b/sh.dir.c
+@@ -1336,7 +1336,7 @@ loaddirs(Char *fname)
+ loaddirs_cmd[1] = fname;
+ else
+ loaddirs_cmd[1] = STRtildotdirs;
+- dosource(loaddirs_cmd, NULL);
++ dosource(loaddirs_cmd, NULL, 0);
+ cleanup_until(&bequiet);
+ }
+
+diff --git a/sh.dol.c b/sh.dol.c
+index bafb971..b9fe82a 100644
+--- a/sh.dol.c
++++ b/sh.dol.c
+@@ -1099,6 +1099,6 @@ again:
+ *obp = 0;
+ tmp = short2str(obuf);
+ (void) xwrite(0, tmp, strlen (tmp));
+- (void) lseek(0, (off_t) 0, L_SET);
++ (void) lseek(0, 0, SEEK_SET);
+ cleanup_until(&inheredoc);
+ }
+diff --git a/sh.err.c b/sh.err.c
+index 279c7b8..9759d72 100644
+--- a/sh.err.c
++++ b/sh.err.c
+@@ -505,6 +505,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;
+diff --git a/sh.h b/sh.h
+index 83a3e93..c05870f 100644
+--- a/sh.h
++++ b/sh.h
+@@ -50,6 +50,23 @@
+ # include <inttypes.h>
+ #endif
+
++#include <unistd.h>
++#include <fcntl.h>
++/*
++ * Source flags (representing -h or -m).
++ *
++ * File locking flags.
++ * - shared and exclusive (read and write) access to a file
++ * - currently used in sh.c and sh.hist.c files while acessing
++ * ~/.history file only.
++ */
++#define SRC_HFLAG 0x01 /* History flag */
++#define SRC_MFLAG 0x02 /* Merge flag */
++#define FD_WRLCK 0x04 /* Write lock */
++#define FD_RDLCK 0x08 /* Read lock */
++#define FD_LEAVE_OPEN 0x10 /* Leave file open */
++#define FD_LEAVE_LCKD 0x20 /* Leave file locked */
++
+ #if !defined(HAVE_STDINT_H) && !defined(HAVE_INTTYPES_H) && !defined(WINNT_NATIVE)
+ typedef unsigned long intptr_t;
+ #endif
+diff --git a/sh.hist.c b/sh.hist.c
+index 2863d5c..f0583cb 100644
+--- a/sh.hist.c
++++ b/sh.hist.c
+@@ -255,7 +255,7 @@ dohist(Char **vp, struct command *c)
+ }
+
+ if (hflg & (HIST_LOAD | HIST_MERGE))
+- loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0);
++ loadhist(*vp, ((hflg & HIST_MERGE) ? SRC_MFLAG : 0) | FD_RDLCK);
+ else if (hflg & HIST_SAVE)
+ rechist(*vp, 1);
+ else {
+@@ -374,8 +374,8 @@ fmthist(int fmt, ptr_t ptr)
+ void
+ rechist(Char *fname, int ref)
+ {
+- Char *snum;
+- int fp, ftmp, oldidfds;
++ Char *snum;
++ int fd = -1, ftmp, oldidfds;
+ struct varent *shist;
+ static Char *dumphist[] = {STRhistory, STRmhT, 0, 0};
+
+@@ -405,15 +405,12 @@ rechist(Char *fname, int ref)
+ * with numerous shells being in simultaneous use. Imagine
+ * any kind of window system. All these shells 'share' the same
+ * ~/.history file for recording their command line history.
+- * Currently the automatic merge can only succeed when the shells
+- * nicely quit one after another.
+- *
+- * Users that like to nuke their environment require here an atomic
+- * loadhist-creat-dohist(dumphist)-close
+- * sequence.
+- *
+- * jw.
+- */
++ *
++ * Atomic merge loadhist-creat/ftrunc-dohist(dumphist)-close
++ * implemented using fcntl (shared readers, exclusive writer)
++ * by Vojtech Vitek (V-Teq) <vvitek@redhat.com>.
++ */
++
+ /*
+ * We need the didfds stuff before loadhist otherwise
+ * exec in a script will fail to print if merge is set.
+@@ -421,41 +418,49 @@ rechist(Char *fname, int ref)
+ */
+ oldidfds = didfds;
+ didfds = 0;
+- if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL)
+- if (shist->vec[1] && eq(shist->vec[1], STRmerge)) {
+- /*
+- * Unset verbose while we read the history file. From:
+- * jbastian@redhat.com (Jeffrey Bastian)
+- */
+- Char *verb = varval(STRverbose);
+- if (verb != STRNULL)
+- unsetv(STRverbose);
+- loadhist(fname, 1);
+- if (verb != STRNULL)
+- setv(STRverbose, verb, VAR_READWRITE);
++ if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL
++ && shist->vec[1] && eq(shist->vec[1], STRmerge)) {
++ /*
++ * Unset verbose while we read the history file. From:
++ * jbastian@redhat.com (Jeffrey Bastian)
++ */
++ Char *verb = varval(STRverbose);
++ if (verb != STRNULL)
++ unsetv(STRverbose);
++ /* Read .history file, leave it's fd open for writing. */
++ fd = loadhist(fname, SRC_MFLAG|FD_WRLCK|FD_LEAVE_OPEN|FD_LEAVE_LCKD);
++ if (fd != -1) {
++ /* Truncate the .history file. */
++ (void) ftruncate(fd, 0);
++ (void) lseek(fd, 0, SEEK_SET);
+ }
+- fp = xcreat(short2str(fname), 0600);
+- if (fp == -1) {
+- didfds = oldidfds;
+- cleanup_until(fname);
+- return;
++ if (verb != STRNULL)
++ setv(STRverbose, verb, VAR_READWRITE);
++ }
++ if (fd == -1) {
++ /* 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);
++ }
++ if (fd != -1) {
++ ftmp = SHOUT;
++ SHOUT = fd;
++ dumphist[2] = snum;
++ /* Write history to the .history file. */
++ dohist(dumphist, NULL);
++ SHOUT = ftmp;
+ }
+- ftmp = SHOUT;
+- SHOUT = fp;
+- dumphist[2] = snum;
+- dohist(dumphist, NULL);
+- xclose(fp);
+- SHOUT = ftmp;
+ didfds = oldidfds;
+ cleanup_until(fname);
+ }
+
+
+-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;
++ loadhist_cmd[1] = (flg & SRC_MFLAG) ? STRmm : STRmh;
+
+ if (fname != NULL)
+ loadhist_cmd[2] = fname;
+@@ -464,5 +469,5 @@ loadhist(Char *fname, int mflg)
+ else
+ loadhist_cmd[2] = STRtildothist;
+
+- dosource(loadhist_cmd, NULL);
++ return dosource(loadhist_cmd, NULL, flg);
+ }
+diff --git a/sh.lex.c b/sh.lex.c
+index 536097e..2543552 100644
+--- a/sh.lex.c
++++ b/sh.lex.c
+@@ -1589,7 +1589,7 @@ wide_read(int fildes, Char *buf, size_t nchars, int use_fclens)
+ /* 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, -partial, SEEK_CUR);
+ errno = err;
+ return res != 0 ? res : r;
+ }
+@@ -1604,7 +1604,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
+@@ -1808,7 +1808,7 @@ btell(struct Ain *l)
+ void
+ btoeof(void)
+ {
+- (void) lseek(SHIN, (off_t) 0, L_XTND);
++ (void) lseek(SHIN, 0, SEEK_END);
+ aret = TCSH_F_SEEK;
+ fseekp = feobp;
+ alvec = NULL;
+@@ -1826,7 +1826,7 @@ settell(void)
+ cantell = 0;
+ if (arginp || onelflg || intty)
+ return;
+- if ((x = lseek(SHIN, (off_t) 0, L_INCR)) == -1)
++ if ((x = lseek(SHIN, 0, SEEK_CUR)) == -1)
+ return;
+ fbuf = xcalloc(2, sizeof(Char **));
+ fblocks = 1;
+diff --git a/sh.sem.c b/sh.sem.c
+index 7c5511a..b8eee96 100644
+--- a/sh.sem.c
++++ b/sh.sem.c
+@@ -879,7 +879,7 @@ doio(struct command *t, int *pipein, int *pipeout)
+ 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, 0, SEEK_END);
+ #endif /* O_APPEND */
+ }
+ else
+--
+1.7.6.2
+
diff --git a/tcsh.spec b/tcsh.spec
index 656f2ec..6497e58 100644
--- a/tcsh.spec
+++ b/tcsh.spec
@@ -43,6 +43,8 @@ Patch26: tcsh-6.17.00-variable-names.patch
Patch27: tcsh-6.17.00-status-pipeline-backquote-list-of-cmds.patch
# Proposed to upstream (http://bugs.gw.com/view.php?id=121)
Patch28: tcsh-6.17.00-manpage-spelling.patch
+Patch30: tcsh-6.17.00-handle-signals-before-flush.patch
+Patch31: tcsh-6.17.00-history-file-locking.patch
Provides: csh = %{version}
Requires(post): grep
@@ -85,6 +87,8 @@ like syntax.
%patch26 -p1 -b .variable-names
%patch27 -p1 -b .status-pipeline-backquote-list-of-cmds
%patch28 -p1 -b .manpage-spelling
+%patch30 -p1 -b .handle-signals-before-flush
+%patch31 -p1 -b .history-file-locking
for i in Fixes WishList; do
iconv -f iso-8859-1 -t utf-8 "$i" > "${i}_" && \
@@ -158,6 +162,12 @@ fi
%{_mandir}/man1/*.1*
%changelog
+* Thu Feb 16 2012 Vojtech Vitek (V-Teq) <vvitek@redhat.com> - 6.17-19
+- Handle pending signals before flush so that the the .history file
+ does not get truncated (#653054)
+- Implement file locking using shared readers, exclusive writer
+ to prevent any .history file data corruption (#653054)
+
* Sat Jan 14 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 6.17-18
- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild