Merge branch 'master' into cde-next
[oweals/cde.git] / cde / lib / tt / mini_isam / isrepair.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                   */
24 /*%%  (c) Copyright 1993, 1994 International Business Machines Corp.     */
25 /*%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                    */
26 /*%%  (c) Copyright 1993, 1994 Novell, Inc.                              */
27 /*%%  $TOG: isrepair.c /main/5 1998/04/10 08:04:42 mgreess $                                                     */
28 /* @(#)isrepair.c       1.7 93/07/30
29  * Copyright (c) 1988, 1993 by Sun Microsystems, Inc.
30  */
31
32 /*
33  * isrepair.c
34  *
35  * Description:
36  *      Repair an ISAM file. 
37  */
38
39
40 #include "isam_impl.h"
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <signal.h>
44 #include <stdio.h>
45
46
47 /*
48  * err =  isrepair(isfname, verbose)
49  *
50  * isrepair repairs an ISAM file. 
51  *
52  * The algorithm used is as the following:
53  *   1. Read the control page of the possibly damaged file. We assume
54  *      that the control page is not damaged.
55  *   2. Open a new ISAM file with ~ suffix.
56  *   3. Scan .rec file (and .var file if variable length records file)
57  *      retrieve all records not marked as deleted, and write them
58  *      to the ~ ISAM file.
59  *   4. Delete the old ISAM file.
60  *   5. Rename ~ file to the original file name.
61  *   6. Build all indexes.
62  *
63  *   verbose option (if set to nonzero) will print messages to stdout.
64  */
65
66 Static  char *rp_readrecord_v(), *rp_readrecord_f();
67 Static int printkey(int, struct keydesc *, int (*)(const char *, ...));
68 Static void cmd_error(const char *, int (*)(const char *, ...));
69 Static int typeletter();
70 Static int rp_readcntlpg();
71 static int  isnoprintf(const char *, ...);
72
73 int isrepair(char *isfname, int verbose)
74 {
75   extern      char *rp_readrecord_v(), *rp_readrecord_f();
76   char        cntlpg[ISCNTLSIZE];
77   int         datfd = -1, indfd = -1, varfd = -1;
78   int         minreclen, maxreclen;
79   int         nrecords_fromcntl;
80   int         varflag;
81   char        nameBuf [MAXPATHLEN];
82   char        *namebuf;
83   int         isfd = -1;
84   struct keydesc2       keydesc2;
85   int         i;
86   long        offset, recfile_end;
87   char        *prec;
88   long        recnum;
89   int         nrecords_found, diff;
90   long        lastrecno;
91   struct keydesc keydesc;
92   int         (*print)(const char *, ...);
93   sigset_t    oldmask;
94   sigset_t    allsignals;
95   char        Buffer[BUFSIZ];
96   char        *buffer = NULL;
97
98
99   print = verbose ? printf : isnoprintf;
100   datfd = indfd = varfd = -1;
101
102   /*
103    * Open UNIX files.
104    */
105   if (strlen(isfname) + 10 >= MAXPATHLEN)
106     namebuf = (char*) malloc(strlen(isfname) + 10);
107   else
108     namebuf = nameBuf;
109
110   (void)strcpy(namebuf, isfname);
111   _makedat_isfname(namebuf);
112   datfd = open(namebuf, O_RDONLY);
113   if (datfd > -1) {
114     if(fcntl(datfd, F_SETFD, 1) == -1) {
115       close(datfd);
116       datfd = -1;
117     }
118   }
119
120   (void)strcpy(namebuf, isfname);
121   _makeind_isfname(namebuf);
122   indfd = open(namebuf, O_RDONLY);
123   if (indfd > -1) {
124     if(fcntl(indfd, F_SETFD, 1) == -1) {
125       close(indfd);
126       indfd = -1;
127     }
128   }
129
130   (void)strcpy(namebuf, isfname);
131   _makevar_isfname(namebuf);
132   varfd = open(namebuf, O_RDONLY);
133   if (varfd > -1) {
134     if(fcntl(varfd, F_SETFD, 1) == -1) {
135        close(varfd);
136        varfd = -1;         
137     }
138   }
139
140   (void)print("Reading control page from %s.rec file...\n",
141               isfname);
142   if (rp_readcntlpg(datfd, cntlpg) == ISERROR) {
143     (void)print("Cannot read the control page\n");
144     goto ERROR;
145   }
146
147   /*
148    * Check magic. Repair only ISAM files!!!
149    */
150
151   if (strncmp(cntlpg + CP_MAGIC_OFF, ISMAGIC, strlen(ISMAGIC)) != 0) {
152     (void)print("Bad magic in %s.rec\n", isfname);
153     goto ERROR;
154   }
155
156   varflag = ldint(cntlpg + CP_VARFLAG_OFF);
157   minreclen = ldint(cntlpg + CP_MINRECLEN_OFF);
158   maxreclen = ldint(cntlpg + CP_MAXRECLEN_OFF); 
159
160   /*
161    *  Check for maxreclen field value of -1. This could have occurred due to
162    *  ISMAXRECLEN being incorrectly set to 65535 in an earlier version. If
163    *  this field is -1 and it's a variable length record, reset to the new
164    *  value of MAXRECLEN. This means that this field will be repaired when
165    *  the control block is written back to disk.
166    */
167
168   if (maxreclen == -1 && varflag) {
169     maxreclen = ISMAXRECLEN;
170   }
171
172   lastrecno = ldlong(cntlpg + CP_LASTRECNO_OFF);        
173   nrecords_fromcntl = ldlong(cntlpg + CP_NRECORDS_OFF);
174
175   /*
176    * Open output file. Use ~ as suffix.
177    */
178   (void)sprintf(namebuf, "%s~", isfname);
179   (void)print("Opening temporary ISAM file '%s'...\n",
180               namebuf);
181   isreclen = minreclen;
182   if ((isfd = isbuild(namebuf, maxreclen, nokey, ISINOUT + ISEXCLLOCK +
183                       (varflag?ISVARLEN:0))) == ISERROR) {
184     (void)print("Cannot open temporary ISAM file %s\n",
185                 namebuf);
186     if (iserrno == EEXIST)
187       (void)print("File %s already exists\n", namebuf);
188     goto ERROR;
189   }
190
191   /*
192    * Scan .rec file and read all undeleted records.
193    */
194   (void)print("Salvaging records from %s.rec%s file...\n",
195               isfname, varflag?" (and .var file)" : "");
196
197   offset = ISCNTLSIZE;
198   recfile_end = lseek(datfd, 0L, 2);
199   recnum = 1;
200   nrecords_found = 0;
201     
202   while (recnum <= lastrecno && offset < recfile_end - minreclen) {
203
204     if (varflag) {
205       prec = rp_readrecord_v(datfd, varfd, offset, minreclen, maxreclen);       
206       offset += minreclen + LONGSIZE;
207     }
208     else {
209       prec = rp_readrecord_f(datfd, offset, minreclen);
210       offset += minreclen + 1;
211     }
212         
213     if (prec != NULL) {
214       if (iswrrec(isfd, recnum, prec) == ISERROR) {
215         cmd_error("iswrrec", print);
216         goto ERROR;
217       }
218       nrecords_found++;
219     }
220     recnum++;
221   }
222
223   diff = nrecords_found - nrecords_fromcntl;
224
225   if (diff == 0)
226     (void)print("All records found - total %d records\n",
227                 nrecords_found);
228   else
229     (void)print("%d records found - %d records %s than in header\n",
230                 nrecords_found, diff, diff > 0 ?
231                 "more" : "less");
232
233   /*
234    * Close all file descriptors.
235    */
236   if(datfd != -1) {
237     close(datfd);
238     datfd = -1;
239   }
240   if(indfd != -1) {
241     close(indfd);
242     indfd = -1;
243   }
244   if(varfd != -1) {
245     close(varfd);
246     varfd = -1;
247   }
248   (void)isclose(isfd);
249
250   (void) sigfillset(&allsignals);
251   (void) sigprocmask(SIG_SETMASK, &allsignals, &oldmask);
252
253   (void)print("Erasing ISAM file '%s'...\n", isfname);
254   /*    if (iserase(isfname) != ISOK) {
255         cmd_error("iserase", print);
256         goto ERROR;
257         }
258         */
259   if (strlen(isfname) + 5 >= MAXPATHLEN)
260     buffer = (char*) malloc(strlen(isfname) + 5);
261   else
262     buffer = Buffer;
263
264   (void)sprintf(buffer,"%s.rec", isfname);
265   (void)unlink(buffer);
266   (void)sprintf(buffer,"%s.ind", isfname);
267   (void)unlink(buffer);
268   (void)sprintf(buffer,"%s.var", isfname);
269   (void)unlink(buffer);
270
271   (void)sprintf(namebuf, "%s~", isfname);
272   (void)print("Renaming ISAM file '%s' to '%s'...\n",
273               namebuf, isfname);
274   if (isrename(namebuf, isfname) != ISOK) {
275     cmd_error("isrename", print);
276     goto ERROR;
277   }
278
279   /*
280    * Re-open the file and add keys.
281    */
282   if (ldshort(cntlpg + CP_NKEYS_OFF) > 0) {
283     (void)print("Adding keys...\n");
284
285     if ((isfd = isopen(isfname, ISEXCLLOCK + ISINOUT +
286                        (varflag?ISVARLEN:0))) == ISERROR) {
287       cmd_error("isopen", print);
288       goto ERROR;
289     }
290         
291     for (i = 0; i < ldshort(cntlpg + CP_NKEYS_OFF); i++) {
292       ldkey(&keydesc2, cntlpg + CP_KEYS_OFF + i * K2_LEN);
293       _iskey_itox(&keydesc2, &keydesc);
294
295       if (keydesc.k_nparts == 0) /* special case for no primary */
296         continue;
297
298       printkey (i+1, &keydesc, print);
299
300       if (i == 0) {
301         if (isaddprimary(isfd, &keydesc) == ISERROR) {
302           cmd_error("isaddprimary", print);
303           (void)isclose(isfd);
304           goto ERROR;
305         }
306       }
307       else {
308         if (isaddindex(isfd, &keydesc) == ISERROR) {
309           cmd_error("isaddindex", print);
310           (void)isclose(isfd);
311           goto ERROR;
312         }
313       }
314     }
315   }
316   (void)isclose(isfd);
317   (void)sigprocmask(SIG_SETMASK, &oldmask, NULL);
318
319   print("...File repaired\n");
320   if (buffer != Buffer) free(buffer);
321   return (ISOK);
322
323  ERROR:
324   (void)print("\007Didn't repair ISAM file '%s'\n", isfname);
325   if(datfd != -1) {
326     close(datfd);
327   }
328   if(indfd != -1) {
329     close(indfd);
330   }
331   if(varfd != -1) {
332     close(varfd);
333   }
334   (void)isclose(isfd);
335   if (namebuf != nameBuf) free(namebuf);
336   if (buffer != Buffer) free(buffer);
337
338   return (ISERROR);
339 }
340
341 /******* low level data access used by the 'repair' utility ******************/
342
343 static char     recordbuffer[ISMAXRECLEN + LONGSIZE];
344
345 /* rp_readcntlpg() - Read the control page */
346 Static int
347 rp_readcntlpg(int datfd, char *cntlpg)
348 {
349     if (read (datfd, cntlpg, ISCNTLSIZE) != ISCNTLSIZE)
350         return (ISERROR);
351
352     return (ISOK);
353 }
354
355 /* rp_readrecord_f() - Read a record from .rec file */
356 Static char *
357 rp_readrecord_f(int datfd, long offset, int reclen)
358 {
359     if (lseek(datfd, offset, 0) != offset)
360         return ((char *) NULL);
361
362     if (read(datfd, recordbuffer, reclen + 1) != (reclen + 1))
363         return ((char *) NULL);
364
365     if (recordbuffer[0] == FL_RECDELETED)
366         return ((char *) NULL);
367
368     return (recordbuffer + 1);
369 }
370
371 /* rp_readrecord_v() - Read a record from .rec file */
372 Static char *
373 rp_readrecord_v(int datfd, int varfd, long offset, int minreclen, int maxreclen)
374 {
375     long        tailoff;
376     char        frameheadbuf [2 * SHORTSIZE];
377     int         taillen;
378     
379     if (lseek(datfd, offset, 0) != offset)
380         return ((char *) NULL);
381
382     if (read(datfd, recordbuffer, minreclen + LONGSIZE) != (minreclen + LONGSIZE))
383         return ((char *) NULL);
384
385     if ((tailoff = ldlong(recordbuffer)) == VL_RECDELETED)
386         return ((char *) NULL);
387
388     isreclen = minreclen;
389
390     /* Recover tail of the record */
391     if (tailoff != VL_RECNOTAIL) {
392
393         if (lseek(varfd, tailoff, 0) != tailoff)
394             goto OKEXIT;
395
396         if (read(varfd, frameheadbuf, 2 * SHORTSIZE) != 2 * SHORTSIZE)
397             goto OKEXIT;
398
399         taillen  = (int) ldshort(frameheadbuf + VR_TAILLEN_OFF);
400
401         if (taillen <= 0 || taillen + minreclen > maxreclen)
402             goto OKEXIT;
403
404         if (read(varfd, recordbuffer + LONGSIZE + isreclen, taillen) != taillen)
405             goto OKEXIT;
406
407         isreclen += taillen;
408     }
409
410  OKEXIT:
411     return (recordbuffer + LONGSIZE);
412 }
413
414 static int
415 isnoprintf(const char *s, ...)
416 {
417     return(0);
418 }
419
420 static int
421 printkey(int n, struct keydesc *pkdesc, int (*print)(const char *, ...))
422 {
423     int         i;
424     struct keypart *pk;
425
426     if (pkdesc->k_nparts == 0) {
427         print("%3d: --- NO PRIMARY KEY ---\n", n);
428         return 0;
429     }
430
431     if (n == 1)
432         print("P%3d: %s ", n, (pkdesc->k_flags&ISDUPS) ?
433               "DUPS  " : "NODUPS");
434     else
435         print(" %3d: %s ", n, (pkdesc->k_flags&ISDUPS) ?
436               "DUPS  " : "NODUPS");
437
438
439     for (i = 0; i < pkdesc->k_nparts; i++) {
440         pk = pkdesc->k_part + i;
441         print(" %d%c%d%s", pk->kp_start, 
442                typeletter(pk->kp_type & ~ISDESC), pk->kp_leng,
443                (pk->kp_type & ISDESC)?"D":" ");
444     }
445     print("\n");
446     return 1;
447 }
448
449 static void
450 cmd_error(const char *str, int (*print)(const char *, ...))
451 {
452     (void)print("%s: ISAM error %d\n", str, iserrno);
453 }
454
455 static int
456 typeletter(int type)
457 {
458     switch (type) {
459     case INTTYPE:
460         return 'I';
461     case LONGTYPE:
462         return 'L';
463     case FLOATTYPE:
464         return 'F';
465     case DOUBLETYPE:
466         return 'D';
467     case CHARTYPE:
468         return 'C';
469     case BINTYPE:
470         return 'B';
471     default:
472         assert(0);
473     }
474     /* NOTREACHED */
475 }