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