xref: /openbsd-src/gnu/usr.bin/cvs/src/filesubr.c (revision 1e72d8d26fae84dfb4bcd4cecabd10b989ec3f29)
1 /* filesubr.c --- subroutines for dealing with files
2    Jim Blandy <jimb@cyclic.com>
3 
4    This file is part of GNU CVS.
5 
6    GNU CVS is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19 
20 /* These functions were moved out of subr.c because they need different
21    definitions under operating systems (like, say, Windows NT) with different
22    file system semantics.  */
23 
24 #include "cvs.h"
25 
26 #ifndef lint
27 static const char rcsid[] = "$CVSid:$";
28 USE(rcsid);
29 #endif
30 
31 /*
32  * I don't know of a convenient way to test this at configure time, or else
33  * I'd certainly do it there.
34  */
35 #if defined(NeXT)
36 #define LOSING_TMPNAM_FUNCTION
37 #endif
38 
39 static int deep_remove_dir PROTO((const char *path));
40 
41 /*
42  * Copies "from" to "to".
43  */
44 void
45 copy_file (from, to)
46     const char *from;
47     const char *to;
48 {
49     struct stat sb;
50     struct utimbuf t;
51     int fdin, fdout;
52 
53     if (trace)
54 #ifdef SERVER_SUPPORT
55 	(void) fprintf (stderr, "%c-> copy(%s,%s)\n",
56 			(server_active) ? 'S' : ' ', from, to);
57 #else
58 	(void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
59 #endif
60     if (noexec)
61 	return;
62 
63     if ((fdin = open (from, O_RDONLY)) < 0)
64 	error (1, errno, "cannot open %s for copying", from);
65     if (fstat (fdin, &sb) < 0)
66 	error (1, errno, "cannot fstat %s", from);
67     if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
68 	error (1, errno, "cannot create %s for copying", to);
69     if (sb.st_size > 0)
70     {
71 	char buf[BUFSIZ];
72 	int n;
73 
74 	for (;;)
75 	{
76 	    n = read (fdin, buf, sizeof(buf));
77 	    if (n == -1)
78 	    {
79 #ifdef EINTR
80 		if (errno == EINTR)
81 		    continue;
82 #endif
83 		error (1, errno, "cannot read file %s for copying", from);
84 	    }
85             else if (n == 0)
86 		break;
87 
88 	    if (write(fdout, buf, n) != n) {
89 		error (1, errno, "cannot write file %s for copying", to);
90 	    }
91 	}
92 
93 #ifdef HAVE_FSYNC
94 	if (fsync (fdout))
95 	    error (1, errno, "cannot fsync file %s after copying", to);
96 #endif
97     }
98 
99     if (close (fdin) < 0)
100 	error (0, errno, "cannot close %s", from);
101     if (close (fdout) < 0)
102 	error (1, errno, "cannot close %s", to);
103 
104     /* now, set the times for the copied file to match those of the original */
105     memset ((char *) &t, 0, sizeof (t));
106     t.actime = sb.st_atime;
107     t.modtime = sb.st_mtime;
108     (void) utime (to, &t);
109 }
110 
111 /* FIXME-krp: these functions would benefit from caching the char * &
112    stat buf.  */
113 
114 /*
115  * Returns non-zero if the argument file is a directory, or is a symbolic
116  * link which points to a directory.
117  */
118 int
119 isdir (file)
120     const char *file;
121 {
122     struct stat sb;
123 
124     if (stat (file, &sb) < 0)
125 	return (0);
126     return (S_ISDIR (sb.st_mode));
127 }
128 
129 /*
130  * Returns non-zero if the argument file is a symbolic link.
131  */
132 int
133 islink (file)
134     const char *file;
135 {
136 #ifdef S_ISLNK
137     struct stat sb;
138 
139     if (lstat (file, &sb) < 0)
140 	return (0);
141     return (S_ISLNK (sb.st_mode));
142 #else
143     return (0);
144 #endif
145 }
146 
147 /*
148  * Returns non-zero if the argument file exists.
149  */
150 int
151 isfile (file)
152     const char *file;
153 {
154     struct stat sb;
155 
156     if (stat (file, &sb) < 0)
157 	return (0);
158     return (1);
159 }
160 
161 /*
162  * Returns non-zero if the argument file is readable.
163  * XXX - must be careful if "cvs" is ever made setuid!
164  */
165 int
166 isreadable (file)
167     const char *file;
168 {
169     return (access (file, R_OK) != -1);
170 }
171 
172 /*
173  * Returns non-zero if the argument file is writable
174  * XXX - muct be careful if "cvs" is ever made setuid!
175  */
176 int
177 iswritable (file)
178     const char *file;
179 {
180     return (access (file, W_OK) != -1);
181 }
182 
183 /*
184  * Open a file and die if it fails
185  */
186 FILE *
187 open_file (name, mode)
188     const char *name;
189     const char *mode;
190 {
191     FILE *fp;
192 
193     if ((fp = fopen (name, mode)) == NULL)
194 	error (1, errno, "cannot open %s", name);
195     return (fp);
196 }
197 
198 /*
199  * Make a directory and die if it fails
200  */
201 void
202 make_directory (name)
203     const char *name;
204 {
205     struct stat buf;
206 
207     if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode)))
208 	    error (0, 0, "%s already exists but is not a directory", name);
209     if (!noexec && mkdir (name, 0777) < 0)
210 	error (1, errno, "cannot make directory %s", name);
211 }
212 
213 /*
214  * Make a path to the argument directory, printing a message if something
215  * goes wrong.
216  */
217 void
218 make_directories (name)
219     const char *name;
220 {
221     char *cp;
222 
223     if (noexec)
224 	return;
225 
226     if (mkdir (name, 0777) == 0 || errno == EEXIST)
227 	return;
228     if (errno != ENOENT)
229     {
230 	error (0, errno, "cannot make path to %s", name);
231 	return;
232     }
233     if ((cp = strrchr (name, '/')) == NULL)
234 	return;
235     *cp = '\0';
236     make_directories (name);
237     *cp++ = '/';
238     if (*cp == '\0')
239 	return;
240     (void) mkdir (name, 0777);
241 }
242 
243 /*
244  * Change the mode of a file, either adding write permissions, or removing
245  * all write permissions.  Adding write permissions honors the current umask
246  * setting.
247  */
248 void
249 xchmod (fname, writable)
250     char *fname;
251     int writable;
252 {
253     struct stat sb;
254     mode_t mode, oumask;
255 
256     if (stat (fname, &sb) < 0)
257     {
258 	if (!noexec)
259 	    error (0, errno, "cannot stat %s", fname);
260 	return;
261     }
262     if (writable)
263     {
264 	oumask = umask (0);
265 	(void) umask (oumask);
266 	mode = sb.st_mode | ~oumask & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) |
267 				       ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) |
268 				       ((sb.st_mode & S_IROTH) ? S_IWOTH : 0));
269     }
270     else
271     {
272 	mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
273     }
274 
275     if (trace)
276 #ifdef SERVER_SUPPORT
277 	(void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
278 			(server_active) ? 'S' : ' ', fname, mode);
279 #else
280 	(void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
281 #endif
282     if (noexec)
283 	return;
284 
285     if (chmod (fname, mode) < 0)
286 	error (0, errno, "cannot change mode of file %s", fname);
287 }
288 
289 /*
290  * Rename a file and die if it fails
291  */
292 void
293 rename_file (from, to)
294     const char *from;
295     const char *to;
296 {
297     if (trace)
298 #ifdef SERVER_SUPPORT
299 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
300 			(server_active) ? 'S' : ' ', from, to);
301 #else
302 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
303 #endif
304     if (noexec)
305 	return;
306 
307     if (rename (from, to) < 0)
308 	error (1, errno, "cannot rename file %s to %s", from, to);
309 }
310 
311 /*
312  * link a file, if possible.
313  */
314 int
315 link_file (from, to)
316     const char *from;
317     const char *to;
318 {
319     if (trace)
320 #ifdef SERVER_SUPPORT
321 	(void) fprintf (stderr, "%c-> link(%s,%s)\n",
322 			(server_active) ? 'S' : ' ', from, to);
323 #else
324 	(void) fprintf (stderr, "-> link(%s,%s)\n", from, to);
325 #endif
326     if (noexec)
327 	return (0);
328 
329     return (link (from, to));
330 }
331 
332 /*
333  * unlink a file, if possible.
334  */
335 int
336 unlink_file (f)
337     const char *f;
338 {
339     if (trace)
340 #ifdef SERVER_SUPPORT
341 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
342 			(server_active) ? 'S' : ' ', f);
343 #else
344 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
345 #endif
346     if (noexec)
347 	return (0);
348 
349     return (unlink (f));
350 }
351 
352 /*
353  * Unlink a file or dir, if possible.  If it is a directory do a deep
354  * removal of all of the files in the directory.  Return -1 on error
355  * (in which case errno is set).
356  */
357 int
358 unlink_file_dir (f)
359     const char *f;
360 {
361     if (trace)
362 #ifdef SERVER_SUPPORT
363 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
364 			(server_active) ? 'S' : ' ', f);
365 #else
366 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
367 #endif
368     if (noexec)
369 	return (0);
370 
371     if (unlink (f) != 0)
372     {
373 	/* under NEXTSTEP errno is set to return EPERM if
374 	 * the file is a directory,or if the user is not
375 	 * allowed to read or write to the file.
376 	 * [This is probably a bug in the O/S]
377 	 * other systems will return EISDIR to indicate
378 	 * that the path is a directory.
379 	 */
380         if (errno == EISDIR || errno == EPERM)
381                 return deep_remove_dir (f);
382         else
383 		/* The file wasn't a directory and some other
384 		 * error occured
385 		 */
386                 return -1;
387     }
388     /* We were able to remove the file from the disk */
389     return 0;
390 }
391 
392 /* Remove a directory and everything it contains.  Returns 0 for
393  * success, -1 for failure (in which case errno is set).
394  */
395 
396 static int
397 deep_remove_dir (path)
398     const char *path;
399 {
400     DIR		  *dirp;
401     struct dirent *dp;
402     char	   buf[PATH_MAX];
403 
404     if ( rmdir (path) != 0 && errno == ENOTEMPTY )
405     {
406 	if ((dirp = opendir (path)) == NULL)
407 	    /* If unable to open the directory return
408 	     * an error
409 	     */
410 	    return -1;
411 
412 	while ((dp = readdir (dirp)) != NULL)
413 	{
414 	    if (strcmp (dp->d_name, ".") == 0 ||
415 			strcmp (dp->d_name, "..") == 0)
416 		continue;
417 
418 	    sprintf (buf, "%s/%s", path, dp->d_name);
419 
420 	    if (unlink (buf) != 0 )
421 	    {
422 		if (errno == EISDIR || errno == EPERM)
423 		{
424 		    if (deep_remove_dir (buf))
425 		    {
426 			closedir (dirp);
427 			return -1;
428 		    }
429 		}
430 		else
431 		{
432 		    /* buf isn't a directory, or there are
433 		     * some sort of permision problems
434 		     */
435 		    closedir (dirp);
436 		    return -1;
437 		}
438 	    }
439 	}
440 	closedir (dirp);
441 	return rmdir (path);
442     }
443     /* Was able to remove the directory return 0 */
444     return 0;
445 }
446 
447 /* Read NCHARS bytes from descriptor FD into BUF.
448    Return the number of characters successfully read.
449    The number returned is always NCHARS unless end-of-file or error.  */
450 static size_t
451 block_read (fd, buf, nchars)
452     int fd;
453     char *buf;
454     size_t nchars;
455 {
456     char *bp = buf;
457     size_t nread;
458 
459     do
460     {
461 	nread = read (fd, bp, nchars);
462 	if (nread == (size_t)-1)
463 	{
464 #ifdef EINTR
465 	    if (errno == EINTR)
466 		continue;
467 #endif
468 	    return (size_t)-1;
469 	}
470 
471 	if (nread == 0)
472 	    break;
473 
474 	bp += nread;
475 	nchars -= nread;
476     } while (nchars != 0);
477 
478     return bp - buf;
479 }
480 
481 
482 /*
483  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
484  */
485 int
486 xcmp (file1, file2)
487     const char *file1;
488     const char *file2;
489 {
490     char *buf1, *buf2;
491     struct stat sb1, sb2;
492     int fd1, fd2;
493     int ret;
494 
495     if ((fd1 = open (file1, O_RDONLY)) < 0)
496 	error (1, errno, "cannot open file %s for comparing", file1);
497     if ((fd2 = open (file2, O_RDONLY)) < 0)
498 	error (1, errno, "cannot open file %s for comparing", file2);
499     if (fstat (fd1, &sb1) < 0)
500 	error (1, errno, "cannot fstat %s", file1);
501     if (fstat (fd2, &sb2) < 0)
502 	error (1, errno, "cannot fstat %s", file2);
503 
504     /* A generic file compare routine might compare st_dev & st_ino here
505        to see if the two files being compared are actually the same file.
506        But that won't happen in CVS, so we won't bother. */
507 
508     if (sb1.st_size != sb2.st_size)
509 	ret = 1;
510     else if (sb1.st_size == 0)
511 	ret = 0;
512     else
513     {
514 	/* FIXME: compute the optimal buffer size by computing the least
515 	   common multiple of the files st_blocks field */
516 	size_t buf_size = 8 * 1024;
517 	size_t read1;
518 	size_t read2;
519 
520 	buf1 = xmalloc (buf_size);
521 	buf2 = xmalloc (buf_size);
522 
523 	do
524 	{
525 	    read1 = block_read (fd1, buf1, buf_size);
526 	    if (read1 == (size_t)-1)
527 		error (1, errno, "cannot read file %s for comparing", file1);
528 
529 	    read2 = block_read (fd2, buf2, buf_size);
530 	    if (read2 == (size_t)-1)
531 		error (1, errno, "cannot read file %s for comparing", file2);
532 
533 	    /* assert (read1 == read2); */
534 
535 	    ret = memcmp(buf1, buf2, read1);
536 	} while (ret == 0 && read1 == buf_size);
537 
538 	free (buf1);
539 	free (buf2);
540     }
541 
542     (void) close (fd1);
543     (void) close (fd2);
544     return (ret);
545 }
546 
547 #ifdef LOSING_TMPNAM_FUNCTION
548 char *tmpnam(char *s)
549 {
550     static char value[L_tmpnam+1];
551 
552     if (s){
553        strcpy(s,"/tmp/cvsXXXXXX");
554        mktemp(s);
555        return s;
556     }else{
557        strcpy(value,"/tmp/cvsXXXXXX");
558        mktemp(s);
559        return value;
560     }
561 }
562 #endif
563 
564 /* Return non-zero iff FILENAME is absolute.
565    Trivial under Unix, but more complicated under other systems.  */
566 int
567 isabsolute (filename)
568     const char *filename;
569 {
570     return filename[0] == '/';
571 }
572 
573 
574 /* Return a pointer into PATH's last component.  */
575 char *
576 last_component (path)
577     char *path;
578 {
579     char *last = strrchr (path, '/');
580 
581     if (last)
582         return last + 1;
583     else
584         return path;
585 }
586