From c8ffd8642254a5ae0fd07524d88a57663417145d Mon Sep 17 00:00:00 2001 From: "Vojtech Vitek (V-Teq)" Date: Thu, 16 Feb 2012 15:47:11 +0100 Subject: [PATCH] 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 --- ...-6.17.00-handle-signals-before-flush.patch | 36 ++ tcsh-6.17.00-history-file-locking.patch | 478 ++++++++++++++++++ tcsh.spec | 10 + 3 files changed, 524 insertions(+) create mode 100644 tcsh-6.17.00-handle-signals-before-flush.patch create mode 100644 tcsh-6.17.00-history-file-locking.patch 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)" +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)" +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) +--- + 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 + #endif + ++#include ++#include ++/* ++ * 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) . ++ */ ++ + /* + * 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) - 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 - 6.17-18 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild