5e9732ce527be4672bfb8fc327682ec710352fb8
[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 static int gotalarm;
24
25 /***************************************************************
26  Signal function to tell us we timed out.
27 ****************************************************************/
28
29 static void gotalarm_sig(void)
30 {
31   gotalarm = 1;
32 }
33
34 /***************************************************************
35  Lock or unlock a fd for a known lock type. Abandon after waitsecs 
36  seconds.
37 ****************************************************************/
38
39 BOOL do_file_lock(int fd, int waitsecs, int type)
40 {
41   SMB_STRUCT_FLOCK lock;
42   int             ret;
43   void (*oldsig_handler)(int);
44
45   gotalarm = 0;
46   oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
47
48   lock.l_type = type;
49   lock.l_whence = SEEK_SET;
50   lock.l_start = 0;
51   lock.l_len = 1;
52   lock.l_pid = 0;
53
54   alarm(waitsecs);
55   /* Note we must *NOT* use sys_fcntl here ! JRA */
56   ret = fcntl(fd, SMB_F_SETLKW, &lock);
57   alarm(0);
58   CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler);
59
60   if (gotalarm) {
61     DEBUG(0, ("do_file_lock: failed to %s file.\n",
62                 type == F_UNLCK ? "unlock" : "lock"));
63     return False;
64   }
65
66   return (ret == 0);
67 }
68
69
70 /***************************************************************
71  Lock an fd. Abandon after waitsecs seconds.
72 ****************************************************************/
73
74 BOOL file_lock(int fd, int type, int secs, int *plock_depth)
75 {
76   if (fd < 0)
77     return False;
78
79   (*plock_depth)++;
80
81   if ((*plock_depth) == 0)
82   {
83     if (!do_file_lock(fd, secs, type)) {
84       DEBUG(10,("file_lock: locking file failed, error = %s.\n",
85                  strerror(errno)));
86       return False;
87     }
88   }
89
90   return True;
91 }
92
93 /***************************************************************
94  Unlock an fd. Abandon after waitsecs seconds.
95 ****************************************************************/
96
97 BOOL file_unlock(int fd, int *plock_depth)
98 {
99   BOOL ret=True;
100
101   if(*plock_depth == 1)
102     ret = do_file_lock(fd, 5, F_UNLCK);
103
104   (*plock_depth)--;
105
106   if(!ret)
107     DEBUG(10,("file_unlock: unlocking file failed, error = %s.\n",
108                  strerror(errno)));
109   return ret;
110 }
111
112 /***************************************************************
113  locks a file for enumeration / modification.
114  update to be set = True if modification is required.
115 ****************************************************************/
116
117 void *startfilepwent(char *pfile, char *s_readbuf, int bufsize,
118                                 int *file_lock_depth, BOOL update)
119 {
120   FILE *fp = NULL;
121
122   if (!*pfile)
123  {
124     DEBUG(0, ("startfilepwent: No file set\n"));
125     return (NULL);
126   }
127   DEBUG(10, ("startfilepwent: opening file %s\n", pfile));
128
129   fp = sys_fopen(pfile, update ? "r+b" : "rb");
130
131   if (fp == NULL) {
132     DEBUG(0, ("startfilepwent: unable to open file %s\n", pfile));
133     return NULL;
134   }
135
136   /* Set a buffer to do more efficient reads */
137   setvbuf(fp, s_readbuf, _IOFBF, bufsize);
138
139   if (!file_lock(fileno(fp), (update ? F_WRLCK : F_RDLCK), 5, file_lock_depth))
140   {
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 void endfilepwent(void *vp, int *file_lock_depth)
157 {
158   FILE *fp = (FILE *)vp;
159
160   file_unlock(fileno(fp), file_lock_depth);
161   fclose(fp);
162   DEBUG(7, ("endfilepwent: closed file.\n"));
163 }
164
165 /*************************************************************************
166  Return the current position in the file list as an SMB_BIG_UINT.
167  This must be treated as an opaque token.
168 *************************************************************************/
169 SMB_BIG_UINT getfilepwpos(void *vp)
170 {
171   return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
172 }
173
174 /*************************************************************************
175  Set the current position in the file list from an SMB_BIG_UINT.
176  This must be treated as an opaque token.
177 *************************************************************************/
178 BOOL setfilepwpos(void *vp, SMB_BIG_UINT tok)
179 {
180   return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
181 }
182
183 /*************************************************************************
184  gets a line out of a file.
185  line is of format "xxxx:xxxxxx:xxxxx:".
186  lines with "#" at the front are ignored.
187 *************************************************************************/
188 int getfileline(void *vp, char *linebuf, int linebuf_size)
189 {
190         /* Static buffers we will return. */
191         FILE *fp = (FILE *)vp;
192         unsigned char   c;
193         unsigned char  *p;
194         size_t            linebuf_len;
195
196         if (fp == NULL)
197         {
198                 DEBUG(0,("getfileline: Bad file pointer.\n"));
199                 return -1;
200         }
201
202         /*
203          * Scan the file, a line at a time.
204          */
205         while (!feof(fp))
206         {
207                 linebuf[0] = '\0';
208
209                 fgets(linebuf, linebuf_size, fp);
210                 if (ferror(fp))
211                 {
212                         return -1;
213                 }
214
215                 /*
216                  * Check if the string is terminated with a newline - if not
217                  * then we must keep reading and discard until we get one.
218                  */
219
220                 linebuf_len = strlen(linebuf);
221                 if (linebuf_len == 0)
222                 {
223                         linebuf[0] = '\0';
224                         return 0;
225                 }
226
227                 if (linebuf[linebuf_len - 1] != '\n')
228                 {
229                         c = '\0';
230                         while (!ferror(fp) && !feof(fp))
231                         {
232                                 c = fgetc(fp);
233                                 if (c == '\n')
234                                 {
235                                         break;
236                                 }
237                         }
238                 }
239                 else
240                 {
241                         linebuf[linebuf_len - 1] = '\0';
242                 }
243
244 #ifdef DEBUG_PASSWORD
245                 DEBUG(100, ("getfileline: got line |%s|\n", linebuf));
246 #endif
247                 if ((linebuf[0] == 0) && feof(fp))
248                 {
249                         DEBUG(4, ("getfileline: end of file reached\n"));
250                         return 0;
251                 }
252
253                 if (linebuf[0] == '#' || linebuf[0] == '\0')
254                 {
255                         DEBUG(6, ("getfileline: skipping comment or blank line\n"));
256                         continue;
257                 }
258
259                 p = (unsigned char *) strchr_m(linebuf, ':');
260                 if (p == NULL)
261                 {
262                         DEBUG(0, ("getfileline: malformed line entry (no :)\n"));
263                         continue;
264                 }
265                 return linebuf_len;
266         }
267         return -1;
268 }
269
270
271 /****************************************************************************
272 read a line from a file with possible \ continuation chars. 
273 Blanks at the start or end of a line are stripped.
274 The string will be allocated if s2 is NULL
275 ****************************************************************************/
276 char *fgets_slash(char *s2,int maxlen,XFILE *f)
277 {
278   char *s=s2;
279   int len = 0;
280   int c;
281   BOOL start_of_line = True;
282
283   if (x_feof(f))
284     return(NULL);
285
286   if (maxlen <2) return(NULL);
287
288   if (!s2)
289     {
290       maxlen = MIN(maxlen,8);
291       s = (char *)malloc(maxlen);
292     }
293
294   if (!s) return(NULL);
295
296   *s = 0;
297
298   while (len < maxlen-1)
299     {
300       c = x_getc(f);
301       switch (c)
302         {
303         case '\r':
304           break;
305         case '\n':
306           while (len > 0 && s[len-1] == ' ')
307             {
308               s[--len] = 0;
309             }
310           if (len > 0 && s[len-1] == '\\')
311             {
312               s[--len] = 0;
313               start_of_line = True;
314               break;
315             }
316           return(s);
317         case EOF:
318           if (len <= 0 && !s2) 
319             SAFE_FREE(s);
320           return(len>0?s:NULL);
321         case ' ':
322           if (start_of_line)
323             break;
324         default:
325           start_of_line = False;
326           s[len++] = c;
327           s[len] = 0;
328         }
329       if (!s2 && len > maxlen-3)
330         {
331           char *t;
332           
333           maxlen *= 2;
334           t = (char *)Realloc(s,maxlen);
335           if (!t) {
336             DEBUG(0,("fgets_slash: failed to expand buffer!\n"));
337             SAFE_FREE(s);
338             return(NULL);
339           } else s = t;
340         }
341     }
342   return(s);
343 }
344
345
346 /****************************************************************************
347 load from a pipe into memory
348 ****************************************************************************/
349 char *file_pload(char *syscmd, size_t *size)
350 {
351         int fd, n;
352         char *p, *tp;
353         pstring buf;
354         size_t total;
355         
356         fd = sys_popen(syscmd);
357         if (fd == -1) return NULL;
358
359         p = NULL;
360         total = 0;
361
362         while ((n = read(fd, buf, sizeof(buf))) > 0) {
363                 tp = Realloc(p, total + n + 1);
364                 if (!tp) {
365                         DEBUG(0,("file_pload: failed to expand buffer!\n"));
366                         close(fd);
367                         SAFE_FREE(p);
368                         return NULL;
369                 } else p = tp;
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) *size = total;
381
382         return p;
383 }
384
385 /****************************************************************************
386 load a file into memory from a fd.
387 ****************************************************************************/ 
388
389 char *fd_load(int fd, size_t *size)
390 {
391         SMB_STRUCT_STAT sbuf;
392         char *p;
393
394         if (sys_fstat(fd, &sbuf) != 0) return NULL;
395
396         p = (char *)malloc(sbuf.st_size+1);
397         if (!p) return NULL;
398
399         if (read(fd, p, sbuf.st_size) != sbuf.st_size) {
400                 SAFE_FREE(p);
401                 return NULL;
402         }
403         p[sbuf.st_size] = 0;
404
405         if (size) *size = sbuf.st_size;
406
407         return p;
408 }
409
410 /****************************************************************************
411 load a file into memory
412 ****************************************************************************/
413 char *file_load(const char *fname, size_t *size)
414 {
415         int fd;
416         char *p;
417
418         if (!fname || !*fname) return NULL;
419         
420         fd = open(fname,O_RDONLY);
421         if (fd == -1) return NULL;
422
423         p = fd_load(fd, size);
424
425         close(fd);
426
427         return p;
428 }
429
430
431 /*******************************************************************
432 mmap (if possible) or read a file
433 ********************************************************************/
434 void *map_file(char *fname, size_t size)
435 {
436         size_t s2 = 0;
437         void *p = NULL;
438 #ifdef HAVE_MMAP
439         if (lp_use_mmap()) {
440                 int fd;
441                 fd = open(fname, O_RDONLY, 0);
442                 if (fd == -1) {
443                         DEBUG(2,("Failed to load %s - %s\n", fname, strerror(errno)));
444                         return NULL;
445                 }
446                 p = mmap(NULL, size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
447                 close(fd);
448                 if (p == MAP_FAILED) {
449                         DEBUG(1,("Failed to mmap %s - %s\n", fname, strerror(errno)));
450                         return NULL;
451                 }
452         }
453 #endif
454         if (!p) {
455                 p = file_load(fname, &s2);
456                 if (!p) return NULL;
457                 if (s2 != size) {
458                         DEBUG(1,("incorrect size for %s - got %l expected %l\n",
459                                  fname, s2, size));
460                         if (p) free(p);
461                         return NULL;
462                 }
463         }
464
465         return p;
466 }
467
468
469 /****************************************************************************
470 parse a buffer into lines
471 ****************************************************************************/
472 static char **file_lines_parse(char *p, size_t size, int *numlines)
473 {
474         int i;
475         char *s, **ret;
476
477         if (!p) return NULL;
478
479         for (s = p, i=0; s < p+size; s++) {
480                 if (s[0] == '\n') i++;
481         }
482
483         ret = (char **)malloc(sizeof(ret[0])*(i+2));
484         if (!ret) {
485                 SAFE_FREE(p);
486                 return NULL;
487         }       
488         memset(ret, 0, sizeof(ret[0])*(i+2));
489         if (numlines) *numlines = i;
490
491         ret[0] = p;
492         for (s = p, i=0; s < p+size; s++) {
493                 if (s[0] == '\n') {
494                         s[0] = 0;
495                         i++;
496                         ret[i] = s+1;
497                 }
498                 if (s[0] == '\r') s[0] = 0;
499         }
500
501         return ret;
502 }
503
504
505 /****************************************************************************
506 load a file into memory and return an array of pointers to lines in the file
507 must be freed with file_lines_free(). 
508 ****************************************************************************/
509 char **file_lines_load(const char *fname, int *numlines)
510 {
511         char *p;
512         size_t size;
513
514         p = file_load(fname, &size);
515         if (!p) return NULL;
516
517         return file_lines_parse(p, size, numlines);
518 }
519
520 /****************************************************************************
521 load a fd into memory and return an array of pointers to lines in the file
522 must be freed with file_lines_free(). If convert is true calls unix_to_dos on
523 the list.
524 ****************************************************************************/
525 char **fd_lines_load(int fd, int *numlines)
526 {
527         char *p;
528         size_t size;
529
530         p = fd_load(fd, &size);
531         if (!p) return NULL;
532
533         return file_lines_parse(p, size, numlines);
534 }
535
536
537 /****************************************************************************
538 load a pipe into memory and return an array of pointers to lines in the data
539 must be freed with file_lines_free(). 
540 ****************************************************************************/
541 char **file_lines_pload(char *syscmd, int *numlines)
542 {
543         char *p;
544         size_t size;
545
546         p = file_pload(syscmd, &size);
547         if (!p) return NULL;
548
549         return file_lines_parse(p, size, numlines);
550 }
551
552 /****************************************************************************
553 free lines loaded with file_lines_load
554 ****************************************************************************/
555 void file_lines_free(char **lines)
556 {
557         if (!lines) return;
558         SAFE_FREE(lines[0]);
559         SAFE_FREE(lines);
560 }
561
562
563 /****************************************************************************
564 take a lislist of lines and modify them to produce a list where \ continues
565 a line
566 ****************************************************************************/
567 void file_lines_slashcont(char **lines)
568 {
569         int i, j;
570
571         for (i=0; lines[i];) {
572                 int len = strlen(lines[i]);
573                 if (lines[i][len-1] == '\\') {
574                         lines[i][len-1] = ' ';
575                         if (lines[i+1]) {
576                                 char *p = &lines[i][len];
577                                 while (p < lines[i+1]) *p++ = ' ';
578                                 for (j = i+1; lines[j]; j++) lines[j] = lines[j+1];
579                         }
580                 } else {
581                         i++;
582                 }
583         }
584 }
585
586 /*
587   save a lump of data into a file. Mostly used for debugging 
588 */
589 BOOL file_save(const char *fname, void *packet, size_t length)
590 {
591         int fd;
592         fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
593         if (fd == -1) {
594                 return False;
595         }
596         if (write(fd, packet, length) != (size_t)length) {
597                 return False;
598         }
599         close(fd);
600         return True;
601 }