303d961df57de7b8b7dfa680d8d2016a87743bf2
[tprouty/samba.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 *)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 *)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 = 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 ****************************************************************************/ 
390
391 char *fd_load(int fd, size_t *size)
392 {
393         SMB_STRUCT_STAT sbuf;
394         char *p;
395
396         if (sys_fstat(fd, &sbuf) != 0) {
397                 return NULL;
398         }
399
400         p = (char *)malloc(sbuf.st_size+1);
401         if (!p) {
402                 return NULL;
403         }
404
405         if (read(fd, p, sbuf.st_size) != sbuf.st_size) {
406                 SAFE_FREE(p);
407                 return NULL;
408         }
409         p[sbuf.st_size] = 0;
410
411         if (size) {
412                 *size = sbuf.st_size;
413         }
414
415         return p;
416 }
417
418 /****************************************************************************
419  Load a file into memory.
420 ****************************************************************************/
421
422 char *file_load(const char *fname, size_t *size)
423 {
424         int fd;
425         char *p;
426
427         if (!fname || !*fname) {
428                 return NULL;
429         }
430         
431         fd = open(fname,O_RDONLY);
432         if (fd == -1) {
433                 return NULL;
434         }
435
436         p = fd_load(fd, size);
437         close(fd);
438         return p;
439 }
440
441 /*******************************************************************
442  mmap (if possible) or read a file.
443 ********************************************************************/
444
445 void *map_file(char *fname, size_t size)
446 {
447         size_t s2 = 0;
448         void *p = NULL;
449 #ifdef HAVE_MMAP
450         int fd;
451         fd = open(fname, O_RDONLY, 0);
452         if (fd == -1) {
453                 DEBUG(2,("map_file: Failed to load %s - %s\n", fname, strerror(errno)));
454                 return NULL;
455         }
456         p = mmap(NULL, size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
457         close(fd);
458         if (p == MAP_FAILED) {
459                 DEBUG(1,("map_file: Failed to mmap %s - %s\n", fname, strerror(errno)));
460                 return NULL;
461         }
462 #endif
463         if (!p) {
464                 p = file_load(fname, &s2);
465                 if (!p) {
466                         return NULL;
467                 }
468                 if (s2 != size) {
469                         DEBUG(1,("map_file: incorrect size for %s - got %lu expected %lu\n",
470                                  fname, (unsigned long)s2, (unsigned long)size));
471                         SAFE_FREE(p);   
472                         return NULL;
473                 }
474         }
475         return p;
476 }
477
478 /****************************************************************************
479  Parse a buffer into lines.
480 ****************************************************************************/
481
482 static char **file_lines_parse(char *p, size_t size, int *numlines)
483 {
484         int i;
485         char *s, **ret;
486
487         if (!p) {
488                 return NULL;
489         }
490
491         for (s = p, i=0; s < p+size; s++) {
492                 if (s[0] == '\n') i++;
493         }
494
495         ret = (char **)malloc(sizeof(ret[0])*(i+2));
496         if (!ret) {
497                 SAFE_FREE(p);
498                 return NULL;
499         }       
500         memset(ret, 0, sizeof(ret[0])*(i+2));
501         if (numlines) {
502                 *numlines = i;
503         }
504
505         ret[0] = p;
506         for (s = p, i=0; s < p+size; s++) {
507                 if (s[0] == '\n') {
508                         s[0] = 0;
509                         i++;
510                         ret[i] = s+1;
511                 }
512                 if (s[0] == '\r') {
513                         s[0] = 0;
514                 }
515         }
516
517         return ret;
518 }
519
520 /****************************************************************************
521  Load a file into memory and return an array of pointers to lines in the file
522  must be freed with file_lines_free(). 
523 ****************************************************************************/
524
525 char **file_lines_load(const char *fname, int *numlines)
526 {
527         char *p;
528         size_t size;
529
530         p = file_load(fname, &size);
531         if (!p) {
532                 return NULL;
533         }
534
535         return file_lines_parse(p, size, numlines);
536 }
537
538 /****************************************************************************
539  Load a fd into memory and return an array of pointers to lines in the file
540  must be freed with file_lines_free(). If convert is true calls unix_to_dos on
541  the list.
542 ****************************************************************************/
543
544 char **fd_lines_load(int fd, int *numlines)
545 {
546         char *p;
547         size_t size;
548
549         p = fd_load(fd, &size);
550         if (!p) {
551                 return NULL;
552         }
553
554         return file_lines_parse(p, size, numlines);
555 }
556
557 /****************************************************************************
558  Load a pipe into memory and return an array of pointers to lines in the data
559  must be freed with file_lines_free(). 
560 ****************************************************************************/
561
562 char **file_lines_pload(char *syscmd, int *numlines)
563 {
564         char *p;
565         size_t size;
566
567         p = file_pload(syscmd, &size);
568         if (!p) {
569                 return NULL;
570         }
571
572         return file_lines_parse(p, size, numlines);
573 }
574
575 /****************************************************************************
576  Free lines loaded with file_lines_load.
577 ****************************************************************************/
578
579 void file_lines_free(char **lines)
580 {
581         if (!lines) {
582                 return;
583         }
584         SAFE_FREE(lines[0]);
585         SAFE_FREE(lines);
586 }
587
588 /****************************************************************************
589  Take a list of lines and modify them to produce a list where \ continues
590  a line.
591 ****************************************************************************/
592
593 void file_lines_slashcont(char **lines)
594 {
595         int i, j;
596
597         for (i=0; lines[i];) {
598                 int len = strlen(lines[i]);
599                 if (lines[i][len-1] == '\\') {
600                         lines[i][len-1] = ' ';
601                         if (lines[i+1]) {
602                                 char *p = &lines[i][len];
603                                 while (p < lines[i+1]) {
604                                         *p++ = ' ';
605                                 }
606                                 for (j = i+1; lines[j]; j++) {
607                                         lines[j] = lines[j+1];
608                                 }
609                         }
610                 } else {
611                         i++;
612                 }
613         }
614 }
615
616 /****************************************************************************
617  Save a lump of data into a file. Mostly used for debugging.
618 ****************************************************************************/
619
620 BOOL file_save(const char *fname, void *packet, size_t length)
621 {
622         int fd;
623         fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
624         if (fd == -1) {
625                 return False;
626         }
627         if (write(fd, packet, length) != (size_t)length) {
628                 return False;
629         }
630         close(fd);
631         return True;
632 }