1 /*
2                                   fitscheck.c
3 
4 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 *
6 *       Part of:        The LDAC Tools
7 *
8 *       Author:         E.BERTIN (IAP)
9 *
10 *       Contents:       Functions related to file integrity
11 *
12 *       Last modify:    15/08/2003
13 *
14 *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 #include	"config.h"
19 #endif
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "fitscat_defs.h"
26 #include "fitscat.h"
27 
28 #define	ENCODE_OFFSET	0x30
29 unsigned int	exclude[13] = {0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
30 				0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60};
31 
32 /****** encode_checksum *****************************************************
33 PROTO	void encode_checksum(unsigned int sum, char *str)
34 PURPOSE	Encode a checksum to ASCII
35 INPUT	Checksum,
36 	Destination string.
37 OUTPUT	-.
38 NOTES	Straightforward copy of  Seaman & Pence 1995
39 	(ftp://iraf.noao.edu/misc/checksum/).
40 AUTHOR	E. Bertin (IAP)
41 VERSION	08/05/2001
42  ***/
encode_checksum(unsigned int sum,char * str)43 void	encode_checksum(unsigned int sum, char *str)
44 
45   {
46    int	ch[4],
47 	i,j,k, byte, check;
48 
49   for (i=0; i<4; i++)
50     {
51 /*-- Each byte becomes four */
52     byte = (sum << 8*i) >> 24;
53     ch[0] = (ch[1] = ch[2] = ch[3] = byte/4 + ENCODE_OFFSET) + (byte%4);
54     for (check=1; check;)		/* avoid ASCII punctuation */
55       for (check=k=0; k<13; k++)
56         {
57         if (ch[0]==exclude[k] || ch[1]==exclude[k])
58           {
59           ch[0]++;
60           ch[1]--;
61           check++;
62           }
63         if (ch[2]==exclude[k] || ch[3]==exclude[k])
64           {
65           ch[2]++;
66           ch[3]--;
67           check++;
68           }
69         }
70     for (j=0; j<4; j++)			/* assign the bytes */
71       str[(4*j+i+1)%16] = ch[j];	/* permute the bytes for FITS */
72     }
73   str[16] = 0;
74 
75   return;
76   }
77 
78 
79 /****** decode_checksum *****************************************************
80 PROTO	unsigned int decode_checksum(char *str)
81 PURPOSE	Decode an ASCII checksum
82 INPUT	Checksum string.
83 OUTPUT	Checksum.
84 NOTES	Straightforward copy of  Seaman & Pence 1995
85 	(ftp://iraf.noao.edu/misc/checksum/).
86 AUTHOR	E. Bertin (IAP)
87 VERSION	08/05/2001
88  ***/
decode_checksum(char * str)89 unsigned int	decode_checksum(char *str)
90 
91   {
92    char			cbuf[16];
93    unsigned short	*sbuf,
94 			los,his;
95    unsigned int		hi,lo, hicarry,locarry;
96    int			i;
97 
98 /* Remove the permuted FITS byte alignment and the ASCII 0 offset */
99   for (i=0; i<16; i++)
100     cbuf[i] = str[(i+1)%16] - 0x30;
101   sbuf = (unsigned short *)cbuf;
102   hi = lo = 0;
103   if (bswapflag)
104     for (i=4; i--;)
105       {
106       his = *(sbuf++);
107       los = *(sbuf++);
108       hi += (*((unsigned char *)&his)<<8) + *((unsigned char *)&his+1);
109       lo += (*((unsigned char *)&los)<<8) + *((unsigned char *)&los+1);
110       }
111   else
112     for (i=4; i--;)
113       {
114       hi += *(sbuf++);
115       lo += *(sbuf++);
116       }
117 
118   hicarry = hi>>16;
119   locarry = lo>>16;
120   while (hicarry || locarry)
121     {
122     hi = (hi & 0xffff) + locarry;
123     lo = (lo & 0xffff) + hicarry;
124     hicarry = hi >> 16;
125     locarry = lo >> 16;
126     }
127 
128   return (hi<<16) + lo;
129   }
130 
131 
132 /****** compute_blocksum *****************************************************
133 PROTO	unsigned int compute_blocksum(char *buf, unsigned int sum)
134 PURPOSE	Compute the checksum of a FITS block (2880 bytes)
135 INPUT	Pointer to the block,
136 	The previous checksum.
137 OUTPUT	The new computed checksum.
138 NOTES	From Seaman & Pence 1995 (ftp://iraf.noao.edu/misc/checksum/). But
139 	contrarily to what is stated by the authors, the original algorithm
140 	depends on the endianity of the machine. The routine below adds
141 	support for ix386-like processors (non-IEEE).
142 AUTHOR	E. Bertin (IAP)
143 VERSION	08/05/2001
144  ***/
compute_blocksum(char * buf,unsigned int sum)145 unsigned int	compute_blocksum(char *buf, unsigned int sum)
146   {
147    unsigned short	*sbuf,
148 			his,los;
149    unsigned int		hi,lo, hicarry,locarry;
150    int			i;
151 
152   sbuf = (unsigned short *)buf;
153   hi = (sum >> 16);
154   lo = (sum << 16) >> 16;
155   if (bswapflag)
156      for (i=FBSIZE/4; i--;)
157       {
158       his = *(sbuf++);
159       los = *(sbuf++);
160       hi += (*((unsigned char *)&his)<<8) + *((unsigned char *)&his+1);
161       lo += (*((unsigned char *)&los)<<8) + *((unsigned char *)&los+1);
162       }
163   else
164     for (i=FBSIZE/4; i--;)
165       {
166       hi += *(sbuf++);
167       lo += *(sbuf++);
168       }
169 
170   hicarry = hi>>16;     /* fold carry bits in */
171   locarry = lo>>16;
172   while (hicarry || locarry)
173     {
174     hi = (hi & 0xFFFF) + locarry;
175     lo = (lo & 0xFFFF) + hicarry;
176     hicarry = hi >> 16;
177     locarry = lo >> 16;
178     }
179 
180   return (hi << 16) + lo;
181   }
182 
183 
184 /****** compute_bodysum *****************************************************
185 PROTO	unsigned int compute_bodysum(tabstruct *tab, unsigned int sum)
186 PURPOSE	Compute the checksum of a FITS body
187 INPUT	Pointer to the tab,
188 	Checksum from a previous iteration.
189 OUTPUT	The computed checksum.
190 NOTES	-.
191 AUTHOR	E. Bertin (IAP)
192 VERSION	15/08/2003
193  ***/
compute_bodysum(tabstruct * tab,unsigned int sum)194 unsigned int	compute_bodysum(tabstruct *tab, unsigned int sum)
195   {
196    catstruct	*cat;
197    char		*buf;
198    KINGSIZE_T	size;
199    int		n, nblock;
200 
201 /* FITS data are generally padded */
202   nblock = (tab->tabsize+FBSIZE-1)/FBSIZE;
203 /* 2 cases: either the data are in memory or still on disk */
204   if (tab->bodybuf)
205     {
206 /*-- In memory: they are probably not padded */
207     buf = (char *)tab->bodybuf;
208     for (n=nblock-1; n--; buf+=FBSIZE)
209       sum = compute_blocksum(buf, sum);
210     if ((size=PADEXTRA(tab->tabsize)))
211       {
212       QCALLOC(buf, char, FBSIZE);
213       size = FBSIZE-size;
214       memcpy(buf, (char *)tab->bodybuf+tab->tabsize-size, size);
215       sum = compute_blocksum(buf, sum);
216       free(buf);
217       }
218     }
219   else
220     {
221 /*-- On disk: they are padded */
222 /*-- We open the file (nothing is done if already open) */
223     if (!(cat=tab->cat))
224       {
225       warning("Cannot access file while computing the checksum in HDU ",
226 		tab->extname);
227       return 0;
228       }
229     open_cat(cat, READ_ONLY);
230     QFSEEK(cat->file, tab->bodypos, SEEK_SET, cat->filename);
231     QMALLOC(buf, char, FBSIZE);
232     for (n=nblock; n--;)
233       {
234       QFREAD(buf, FBSIZE, cat->file, cat->filename);
235 /*---- No need to swap bytes */
236       sum = compute_blocksum(buf, sum);
237       }
238     }
239 
240   return sum;
241   }
242 
243 
244 /****** write_checksum *****************************************************
245 PROTO	void write_checksum(tabstruct *tab)
246 PURPOSE	Compute and write the checksum to a FITS table
247 INPUT	Pointer to the tab.
248 OUTPUT	-.
249 NOTES	-.
250 AUTHOR	E. Bertin (IAP)
251 VERSION	04/06/2001
252  ***/
write_checksum(tabstruct * tab)253 void	write_checksum(tabstruct *tab)
254 
255   {
256    char		str[32],
257 		*buf;
258    unsigned int	sum;
259    int		i;
260 
261 /* Keep some margin */
262   QREALLOC(tab->headbuf, char, 80*(tab->headnblock*36+3));
263 /* Add or update keywords in the header */
264   fitsadd(tab->headbuf, "CHECKSUM", "ASCII 1's complement checksum");
265   fitswrite(tab->headbuf, "CHECKSUM", "0000000000000000",
266 	H_STRING, T_STRING);
267   fitsadd(tab->headbuf, "DATASUM ", "Checksum of data records");
268   fitswrite(tab->headbuf, "DATASUM ", "0", H_STRING, T_STRING);
269   fitsadd(tab->headbuf, "CHECKVER", "Checksum version ID");
270   fitswrite(tab->headbuf, "CHECKVER", "COMPLEMENT", H_STRING, T_STRING);
271 /* Keep only what's necessary */
272   tab->headnblock = ((fitsfind(tab->headbuf, "END     ")+36)*80)/FBSIZE;
273   QREALLOC(tab->headbuf, char, tab->headnblock*FBSIZE);
274 /* First: the data */
275   tab->bodysum = sum = compute_bodysum(tab, 0);
276   sprintf(str, "%u", sum);
277   fitswrite(tab->headbuf, "DATASUM ", str, H_STRING, T_STRING);
278 
279 
280 /* Now the header */
281   buf = tab->headbuf;
282   for (i=tab->headnblock; i--; buf+=FBSIZE)
283     sum = compute_blocksum(buf, sum);
284 
285 /* Complement to 1 */
286   encode_checksum(~sum, str);
287   fitswrite(tab->headbuf, "CHECKSUM", str, H_STRING, T_STRING);
288 
289   return;
290   }
291 
292 
293 /****** verify_checksum *****************************************************
294 PROTO	int verify_checksum(tabstruct *tab)
295 PURPOSE	Compute and check the checksum of a FITS table
296 INPUT	Pointer to the tab.
297 OUTPUT	RETURN_OK if the checksum is correct, RETURN_ERROR if it is
298 	incorrect, or RETURN_FATAL_ERROR if no checksum found.
299 NOTES	-.
300 AUTHOR	E. Bertin (IAP)
301 VERSION	07/05/2001
302  ***/
verify_checksum(tabstruct * tab)303 int	verify_checksum(tabstruct *tab)
304 
305   {
306    char		*buf;
307    unsigned int	sum;
308    int		i;
309 
310   if (fitsfind(tab->headbuf, "CHECKSUM")==RETURN_ERROR)
311     return RETURN_FATAL_ERROR;
312 
313 /* First: the data */
314   sum = compute_bodysum(tab, 0);
315 /* Now the header */
316   buf = tab->headbuf;
317   for (i=tab->headnblock; i--; buf+=FBSIZE)
318     sum = compute_blocksum(buf, sum);
319 /* The result should sum to 0 */
320   sum = ~sum;
321 
322   return sum? RETURN_ERROR : RETURN_OK;
323   }
324 
325