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