r13316: Let the carnage begin....
[sfrench/samba-autobuild/.git] / source / lib / util_file.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
5  * 
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  * 
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with
17  * this program; if not, write to the Free Software Foundation, Inc., 675
18  * Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "includes.h"
22
23 #ifndef MAP_FAILED
24 #define MAP_FAILED ((void *)-1)
25 #endif
26
27 static int gotalarm;
28
29 /***************************************************************
30  Signal function to tell us we timed out.
31 ****************************************************************/
32
33 static void gotalarm_sig(void)
34 {
35         gotalarm = 1;
36 }
37
38 /***************************************************************
39  Lock or unlock a fd for a known lock type. Abandon after waitsecs 
40  seconds.
41 ****************************************************************/
42
43 BOOL do_file_lock(int fd, int waitsecs, int type)
44 {
45         SMB_STRUCT_FLOCK lock;
46         int             ret;
47         void (*oldsig_handler)(int);
48
49         gotalarm = 0;
50         oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
51
52         lock.l_type = type;
53         lock.l_whence = SEEK_SET;
54         lock.l_start = 0;
55         lock.l_len = 1;
56         lock.l_pid = 0;
57
58         alarm(waitsecs);
59         /* Note we must *NOT* use sys_fcntl here ! JRA */
60         ret = fcntl(fd, SMB_F_SETLKW, &lock);
61         alarm(0);
62         CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler);
63
64         if (gotalarm) {
65                 DEBUG(0, ("do_file_lock: failed to %s file.\n",
66                         type == F_UNLCK ? "unlock" : "lock"));
67                 return False;
68         }
69
70         return (ret == 0);
71 }
72
73 /***************************************************************
74  Lock an fd. Abandon after waitsecs seconds.
75 ****************************************************************/
76
77 BOOL file_lock(int fd, int type, int secs, int *plock_depth)
78 {
79         if (fd < 0)
80                 return False;
81
82         (*plock_depth)++;
83
84         if ((*plock_depth) == 0) {
85                 if (!do_file_lock(fd, secs, type)) {
86                         DEBUG(10,("file_lock: locking file failed, error = %s.\n", strerror(errno)));
87                         return False;
88                 }
89         }
90
91         return True;
92 }
93
94 /***************************************************************
95  Unlock an fd. Abandon after waitsecs seconds.
96 ****************************************************************/
97
98 BOOL file_unlock(int fd, int *plock_depth)
99 {
100         BOOL ret=True;
101
102         if(*plock_depth == 1) {
103                 ret = do_file_lock(fd, 5, F_UNLCK);
104         }
105
106         (*plock_depth)--;
107
108         if(!ret) {
109                 DEBUG(10,("file_unlock: unlocking file failed, error = %s.\n", strerror(errno)));
110         }
111         return ret;
112 }
113
114 /***************************************************************
115  Locks a file for enumeration / modification.
116  update to be set = True if modification is required.
117 ****************************************************************/
118
119 void *startfilepwent(char *pfile, char *s_readbuf, int bufsize,
120                                 int *file_lock_depth, BOOL update)
121 {
122         FILE *fp = NULL;
123
124         if (!*pfile) {
125                 DEBUG(0, ("startfilepwent: No file set\n"));
126                 return (NULL);
127         }
128         DEBUG(10, ("startfilepwent: opening file %s\n", pfile));
129
130         fp = sys_fopen(pfile, update ? "r+b" : "rb");
131
132         if (fp == NULL) {
133                 DEBUG(0, ("startfilepwent: unable to open file %s\n", pfile));
134                 return NULL;
135         }
136
137         /* Set a buffer to do more efficient reads */
138         setvbuf(fp, s_readbuf, _IOFBF, bufsize);
139
140         if (!file_lock(fileno(fp), (update ? F_WRLCK : F_RDLCK), 5, file_lock_depth)) {
141                 DEBUG(0, ("startfilepwent: unable to lock file %s\n", pfile));
142                 fclose(fp);
143                 return NULL;
144         }
145
146         /* Make sure it is only rw by the owner */
147         chmod(pfile, 0600);
148
149         /* We have a lock on the file. */
150         return (void *)fp;
151 }
152
153 /***************************************************************
154  End enumeration of the file.
155 ****************************************************************/
156
157 void endfilepwent(void *vp, int *file_lock_depth)
158 {
159         FILE *fp = (FILE *)vp;
160
161         file_unlock(fileno(fp), file_lock_depth);
162         fclose(fp);
163         DEBUG(7, ("endfilepwent: closed file.\n"));
164 }
165
166 /*************************************************************************
167  Return the current position in the file list as an SMB_BIG_UINT.
168  This must be treated as an opaque token.
169 *************************************************************************/
170
171 SMB_BIG_UINT getfilepwpos(void *vp)
172 {
173         return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
174 }
175
176 /*************************************************************************
177  Set the current position in the file list from an SMB_BIG_UINT.
178  This must be treated as an opaque token.
179 *************************************************************************/
180
181 BOOL setfilepwpos(void *vp, SMB_BIG_UINT tok)
182 {
183         return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
184 }
185
186 /*************************************************************************
187  Gets a line out of a file.
188  line is of format "xxxx:xxxxxx:xxxxx:".
189  lines with "#" at the front are ignored.
190 *************************************************************************/
191
192 int getfileline(void *vp, char *linebuf, int linebuf_size)
193 {
194         /* Static buffers we will return. */
195         FILE *fp = (FILE *)vp;
196         unsigned char   c;
197         unsigned char  *p;
198         size_t linebuf_len;
199
200         if (fp == NULL) {
201                 DEBUG(0,("getfileline: Bad file pointer.\n"));
202                 return -1;
203         }
204
205         /*
206          * Scan the file, a line at a time.
207          */
208         while (!feof(fp)) {
209                 linebuf[0] = '\0';
210
211                 fgets(linebuf, linebuf_size, fp);
212                 if (ferror(fp)) {
213                         return -1;
214                 }
215
216                 /*
217                  * Check if the string is terminated with a newline - if not
218                  * then we must keep reading and discard until we get one.
219                  */
220
221                 linebuf_len = strlen(linebuf);
222                 if (linebuf_len == 0) {
223                         linebuf[0] = '\0';
224                         return 0;
225                 }
226
227                 if (linebuf[linebuf_len - 1] != '\n') {
228                         c = '\0';
229                         while (!ferror(fp) && !feof(fp)) {
230                                 c = fgetc(fp);
231                                 if (c == '\n') {
232                                         break;
233                                 }
234                         }
235                 } else {
236                         linebuf[linebuf_len - 1] = '\0';
237                 }
238
239 #ifdef DEBUG_PASSWORD
240                 DEBUG(100, ("getfileline: got line |%s|\n", linebuf));
241 #endif
242                 if ((linebuf[0] == 0) && feof(fp)) {
243                         DEBUG(4, ("getfileline: end of file reached\n"));
244                         return 0;
245                 }
246
247                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
248                         DEBUG(6, ("getfileline: skipping comment or blank line\n"));
249                         continue;
250                 }
251
252                 p = (unsigned char *) strchr_m(linebuf, ':');
253                 if (p == NULL) {
254                         DEBUG(0, ("getfileline: malformed line entry (no :)\n"));
255                         continue;
256                 }
257                 return linebuf_len;
258         }
259         return -1;
260 }
261
262 /****************************************************************************
263  Read a line from a file with possible \ continuation chars. 
264  Blanks at the start or end of a line are stripped.
265  The string will be allocated if s2 is NULL.
266 ****************************************************************************/
267
268 char *fgets_slash(char *s2,int maxlen,XFILE *f)
269 {
270         char *s=s2;
271         int len = 0;
272         int c;
273         BOOL start_of_line = True;
274
275         if (x_feof(f)) {
276                 return(NULL);
277         }
278
279         if (maxlen <2) {
280                 return(NULL);
281         }
282
283         if (!s2) {
284                 maxlen = MIN(maxlen,8);
285                 s = (char *)SMB_MALLOC(maxlen);
286         }
287
288         if (!s) {
289                 return(NULL);
290         }
291
292         *s = 0;
293
294         while (len < maxlen-1) {
295                 c = x_getc(f);
296                 switch (c) {
297                         case '\r':
298                                 break;
299                         case '\n':
300                                 while (len > 0 && s[len-1] == ' ') {
301                                         s[--len] = 0;
302                                 }
303                                 if (len > 0 && s[len-1] == '\\') {
304                                         s[--len] = 0;
305                                         start_of_line = True;
306                                         break;
307                                 }
308                                 return(s);
309                         case EOF:
310                                 if (len <= 0 && !s2)  {
311                                         SAFE_FREE(s);
312                                 }
313                                 return(len>0?s:NULL);
314                         case ' ':
315                                 if (start_of_line) {
316                                         break;
317                                 }
318                         default:
319                                 start_of_line = False;
320                                 s[len++] = c;
321                                 s[len] = 0;
322                 }
323
324                 if (!s2 && len > maxlen-3) {
325                         char *t;
326           
327                         maxlen *= 2;
328                         t = (char *)SMB_REALLOC(s,maxlen);
329                         if (!t) {
330                                 DEBUG(0,("fgets_slash: failed to expand buffer!\n"));
331                                 SAFE_FREE(s);
332                                 return(NULL);
333                         } else {
334                                 s = t;
335                         }
336                 }
337         }
338         return(s);
339 }
340
341 /****************************************************************************
342  Load from a pipe into memory.
343 ****************************************************************************/
344
345 char *file_pload(char *syscmd, size_t *size)
346 {
347         int fd, n;
348         char *p, *tp;
349         pstring buf;
350         size_t total;
351         
352         fd = sys_popen(syscmd);
353         if (fd == -1) {
354                 return NULL;
355         }
356
357         p = NULL;
358         total = 0;
359
360         while ((n = read(fd, buf, sizeof(buf))) > 0) {
361                 tp = SMB_REALLOC(p, total + n + 1);
362                 if (!tp) {
363                         DEBUG(0,("file_pload: failed to expand buffer!\n"));
364                         close(fd);
365                         SAFE_FREE(p);
366                         return NULL;
367                 } else {
368                         p = tp;
369                 }
370                 memcpy(p+total, buf, n);
371                 total += n;
372         }
373         if (p) p[total] = 0;
374
375         /* FIXME: Perhaps ought to check that the command completed
376          * successfully (returned 0); if not the data may be
377          * truncated. */
378         sys_pclose(fd);
379
380         if (size) {
381                 *size = total;
382         }
383
384         return p;
385 }
386
387 /****************************************************************************
388  Load a file into memory from a fd.
389  Truncate at maxsize. If maxsize == 0 - no limit.
390 ****************************************************************************/ 
391
392 char *fd_load(int fd, size_t *psize, size_t maxsize)
393 {
394         SMB_STRUCT_STAT sbuf;
395         size_t size;
396         char *p;
397
398         if (sys_fstat(fd, &sbuf) != 0) {
399                 return NULL;
400         }
401
402         size = sbuf.st_size;
403         if (maxsize) {
404                 size = MIN(size, maxsize);
405         }
406
407         p = (char *)SMB_MALLOC(size+1);
408         if (!p) {
409                 return NULL;
410         }
411
412         if (read(fd, p, size) != size) {
413                 SAFE_FREE(p);
414                 return NULL;
415         }
416         p[size] = 0;
417
418         if (psize) {
419                 *psize = size;
420         }
421
422         return p;
423 }
424
425 /****************************************************************************
426  Load a file into memory.
427 ****************************************************************************/
428
429 char *file_load(const char *fname, size_t *size, size_t maxsize)
430 {
431         int fd;
432         char *p;
433
434         if (!fname || !*fname) {
435                 return NULL;
436         }
437         
438         fd = open(fname,O_RDONLY);
439         if (fd == -1) {
440                 return NULL;
441         }
442
443         p = fd_load(fd, size, maxsize);
444         close(fd);
445         return p;
446 }
447
448 /*******************************************************************
449  mmap (if possible) or read a file.
450 ********************************************************************/
451
452 void *map_file(char *fname, size_t size)
453 {
454         size_t s2 = 0;
455         void *p = NULL;
456 #ifdef HAVE_MMAP
457         int fd;
458         fd = open(fname, O_RDONLY, 0);
459         if (fd == -1) {
460                 DEBUG(2,("map_file: Failed to load %s - %s\n", fname, strerror(errno)));
461                 return NULL;
462         }
463         p = mmap(NULL, size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
464         close(fd);
465         if (p == MAP_FAILED) {
466                 DEBUG(1,("map_file: Failed to mmap %s - %s\n", fname, strerror(errno)));
467                 return NULL;
468         }
469 #endif
470         if (!p) {
471                 p = file_load(fname, &s2, 0);
472                 if (!p) {
473                         return NULL;
474                 }
475                 if (s2 != size) {
476                         DEBUG(1,("map_file: incorrect size for %s - got %lu expected %lu\n",
477                                  fname, (unsigned long)s2, (unsigned long)size));
478                         SAFE_FREE(p);   
479                         return NULL;
480                 }
481         }
482         return p;
483 }
484
485 /****************************************************************************
486  Parse a buffer into lines.
487 ****************************************************************************/
488
489 static char **file_lines_parse(char *p, size_t size, int *numlines)
490 {
491         int i;
492         char *s, **ret;
493
494         if (!p) {
495                 return NULL;
496         }
497
498         for (s = p, i=0; s < p+size; s++) {
499                 if (s[0] == '\n') i++;
500         }
501
502         ret = SMB_MALLOC_ARRAY(char *, i+2);
503         if (!ret) {
504                 SAFE_FREE(p);
505                 return NULL;
506         }       
507         memset(ret, 0, sizeof(ret[0])*(i+2));
508         if (numlines) {
509                 *numlines = i;
510         }
511
512         ret[0] = p;
513         for (s = p, i=0; s < p+size; s++) {
514                 if (s[0] == '\n') {
515                         s[0] = 0;
516                         i++;
517                         ret[i] = s+1;
518                 }
519                 if (s[0] == '\r') {
520                         s[0] = 0;
521                 }
522         }
523
524         return ret;
525 }
526
527 /****************************************************************************
528  Load a file into memory and return an array of pointers to lines in the file
529  must be freed with file_lines_free(). 
530 ****************************************************************************/
531
532 char **file_lines_load(const char *fname, int *numlines, size_t maxsize)
533 {
534         char *p;
535         size_t size = 0;
536
537         p = file_load(fname, &size, maxsize);
538         if (!p) {
539                 return NULL;
540         }
541
542         return file_lines_parse(p, size, numlines);
543 }
544
545 /****************************************************************************
546  Load a fd into memory and return an array of pointers to lines in the file
547  must be freed with file_lines_free(). If convert is true calls unix_to_dos on
548  the list.
549 ****************************************************************************/
550
551 char **fd_lines_load(int fd, int *numlines, size_t maxsize)
552 {
553         char *p;
554         size_t size;
555
556         p = fd_load(fd, &size, maxsize);
557         if (!p) {
558                 return NULL;
559         }
560
561         return file_lines_parse(p, size, numlines);
562 }
563
564 /****************************************************************************
565  Load a pipe into memory and return an array of pointers to lines in the data
566  must be freed with file_lines_free(). 
567 ****************************************************************************/
568
569 char **file_lines_pload(char *syscmd, int *numlines)
570 {
571         char *p;
572         size_t size;
573
574         p = file_pload(syscmd, &size);
575         if (!p) {
576                 return NULL;
577         }
578
579         return file_lines_parse(p, size, numlines);
580 }
581
582 /****************************************************************************
583  Free lines loaded with file_lines_load.
584 ****************************************************************************/
585
586 void file_lines_free(char **lines)
587 {
588         if (!lines) {
589                 return;
590         }
591         SAFE_FREE(lines[0]);
592         SAFE_FREE(lines);
593 }
594
595 /****************************************************************************
596  Take a list of lines and modify them to produce a list where \ continues
597  a line.
598 ****************************************************************************/
599
600 void file_lines_slashcont(char **lines)
601 {
602         int i, j;
603
604         for (i=0; lines[i];) {
605                 int len = strlen(lines[i]);
606                 if (lines[i][len-1] == '\\') {
607                         lines[i][len-1] = ' ';
608                         if (lines[i+1]) {
609                                 char *p = &lines[i][len];
610                                 while (p < lines[i+1]) {
611                                         *p++ = ' ';
612                                 }
613                                 for (j = i+1; lines[j]; j++) {
614                                         lines[j] = lines[j+1];
615                                 }
616                         }
617                 } else {
618                         i++;
619                 }
620         }
621 }
622
623 /****************************************************************************
624  Save a lump of data into a file. Mostly used for debugging.
625 ****************************************************************************/
626
627 BOOL file_save(const char *fname, void *packet, size_t length)
628 {
629         int fd;
630         fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
631         if (fd == -1) {
632                 return False;
633         }
634         if (write(fd, packet, length) != (size_t)length) {
635                 return False;
636         }
637         close(fd);
638         return True;
639 }