xref: /openbsd-src/gnu/usr.bin/cvs/src/filesubr.c (revision 13571821e83933f3c1d7fd1ab5ff9cd54f0eea7f)
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     return isaccessible(file, F_OK);
155 }
156 
157 /*
158  * Returns non-zero if the argument file is readable.
159  */
160 int
161 isreadable (file)
162     const char *file;
163 {
164     return isaccessible(file, R_OK);
165 }
166 
167 /*
168  * Returns non-zero if the argument file is writable.
169  */
170 int
171 iswritable (file)
172     const char *file;
173 {
174     return isaccessible(file, W_OK);
175 }
176 
177 /*
178  * Returns non-zero if the argument file is accessable according to
179  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
180  * bits set.
181  */
182 int
183 isaccessible (file, mode)
184     const char *file;
185     const int mode;
186 {
187 #ifdef SETXID_SUPPORT
188     struct stat sb;
189     int umask = 0;
190     int gmask = 0;
191     int omask = 0;
192     int uid;
193 
194     if (stat(file, &sb) == -1)
195 	return 0;
196     if (mode == F_OK)
197 	return 1;
198 
199     uid = geteuid();
200     if (uid == 0)		/* superuser */
201     {
202 	if (mode & X_OK)
203 	    return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
204 	else
205 	    return 1;
206     }
207 
208     if (mode & R_OK)
209     {
210 	umask |= S_IRUSR;
211 	gmask |= S_IRGRP;
212 	omask |= S_IROTH;
213     }
214     if (mode & W_OK)
215     {
216 	umask |= S_IWUSR;
217 	gmask |= S_IWGRP;
218 	omask |= S_IWOTH;
219     }
220     if (mode & X_OK)
221     {
222 	umask |= S_IXUSR;
223 	gmask |= S_IXGRP;
224 	omask |= S_IXOTH;
225     }
226 
227     if (sb.st_uid == uid)
228 	return (sb.st_mode & umask) == umask;
229     else if (sb.st_gid == getegid())
230 	return (sb.st_mode & gmask) == gmask;
231     else
232 	return (sb.st_mode & omask) == omask;
233 #else
234     return access(file, mode) == 0;
235 #endif
236 }
237 
238 /*
239  * Open a file and die if it fails
240  */
241 FILE *
242 open_file (name, mode)
243     const char *name;
244     const char *mode;
245 {
246     FILE *fp;
247 
248     if ((fp = fopen (name, mode)) == NULL)
249 	error (1, errno, "cannot open %s", name);
250     return (fp);
251 }
252 
253 /*
254  * Make a directory and die if it fails
255  */
256 void
257 make_directory (name)
258     const char *name;
259 {
260     struct stat sb;
261 
262     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
263 	    error (0, 0, "%s already exists but is not a directory", name);
264     if (!noexec && mkdir (name, 0777) < 0)
265 	error (1, errno, "cannot make directory %s", name);
266 }
267 
268 /*
269  * Make a path to the argument directory, printing a message if something
270  * goes wrong.
271  */
272 void
273 make_directories (name)
274     const char *name;
275 {
276     char *cp;
277 
278     if (noexec)
279 	return;
280 
281     if (mkdir (name, 0777) == 0 || errno == EEXIST)
282 	return;
283     if (! existence_error (errno))
284     {
285 	error (0, errno, "cannot make path to %s", name);
286 	return;
287     }
288     if ((cp = strrchr (name, '/')) == NULL)
289 	return;
290     *cp = '\0';
291     make_directories (name);
292     *cp++ = '/';
293     if (*cp == '\0')
294 	return;
295     (void) mkdir (name, 0777);
296 }
297 
298 /*
299  * Change the mode of a file, either adding write permissions, or removing
300  * all write permissions.  Either change honors the current umask setting.
301  */
302 void
303 xchmod (fname, writable)
304     char *fname;
305     int writable;
306 {
307     struct stat sb;
308     mode_t mode, oumask;
309 
310     if (stat (fname, &sb) < 0)
311     {
312 	if (!noexec)
313 	    error (0, errno, "cannot stat %s", fname);
314 	return;
315     }
316     oumask = umask (0);
317     (void) umask (oumask);
318     if (writable)
319     {
320 	mode = sb.st_mode | (~oumask
321 			     & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
322 				| ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
323 				| ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
324     }
325     else
326     {
327 	mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
328     }
329 
330     if (trace)
331 #ifdef SERVER_SUPPORT
332 	(void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
333 			(server_active) ? 'S' : ' ', fname, mode);
334 #else
335 	(void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
336 #endif
337     if (noexec)
338 	return;
339 
340     if (chmod (fname, mode) < 0)
341 	error (0, errno, "cannot change mode of file %s", fname);
342 }
343 
344 /*
345  * Rename a file and die if it fails
346  */
347 void
348 rename_file (from, to)
349     const char *from;
350     const char *to;
351 {
352     if (trace)
353 #ifdef SERVER_SUPPORT
354 	(void) fprintf (stderr, "%c-> rename(%s,%s)\n",
355 			(server_active) ? 'S' : ' ', from, to);
356 #else
357 	(void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
358 #endif
359     if (noexec)
360 	return;
361 
362     if (rename (from, to) < 0)
363 	error (1, errno, "cannot rename file %s to %s", from, to);
364 }
365 
366 /*
367  * link a file, if possible.  Warning: the Windows NT version of this
368  * function just copies the file, so only use this function in ways
369  * that can deal with either a link or a copy.
370  */
371 int
372 link_file (from, to)
373     const char *from;
374     const char *to;
375 {
376     if (trace)
377 #ifdef SERVER_SUPPORT
378 	(void) fprintf (stderr, "%c-> link(%s,%s)\n",
379 			(server_active) ? 'S' : ' ', from, to);
380 #else
381 	(void) fprintf (stderr, "-> link(%s,%s)\n", from, to);
382 #endif
383     if (noexec)
384 	return (0);
385 
386     return (link (from, to));
387 }
388 
389 /*
390  * unlink a file, if possible.
391  */
392 int
393 unlink_file (f)
394     const char *f;
395 {
396     if (trace)
397 #ifdef SERVER_SUPPORT
398 	(void) fprintf (stderr, "%c-> unlink(%s)\n",
399 			(server_active) ? 'S' : ' ', f);
400 #else
401 	(void) fprintf (stderr, "-> unlink(%s)\n", f);
402 #endif
403     if (noexec)
404 	return (0);
405 
406     return (unlink (f));
407 }
408 
409 /*
410  * Unlink a file or dir, if possible.  If it is a directory do a deep
411  * removal of all of the files in the directory.  Return -1 on error
412  * (in which case errno is set).
413  */
414 int
415 unlink_file_dir (f)
416     const char *f;
417 {
418     if (trace)
419 #ifdef SERVER_SUPPORT
420 	(void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
421 			(server_active) ? 'S' : ' ', f);
422 #else
423 	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
424 #endif
425     if (noexec)
426 	return (0);
427 
428     if (unlink (f) != 0)
429     {
430 	/* under NEXTSTEP errno is set to return EPERM if
431 	 * the file is a directory,or if the user is not
432 	 * allowed to read or write to the file.
433 	 * [This is probably a bug in the O/S]
434 	 * other systems will return EISDIR to indicate
435 	 * that the path is a directory.
436 	 */
437         if (errno == EISDIR || errno == EPERM)
438                 return deep_remove_dir (f);
439         else
440 		/* The file wasn't a directory and some other
441 		 * error occured
442 		 */
443                 return -1;
444     }
445     /* We were able to remove the file from the disk */
446     return 0;
447 }
448 
449 /* Remove a directory and everything it contains.  Returns 0 for
450  * success, -1 for failure (in which case errno is set).
451  */
452 
453 static int
454 deep_remove_dir (path)
455     const char *path;
456 {
457     DIR		  *dirp;
458     struct dirent *dp;
459     char	   buf[PATH_MAX];
460 
461     if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST))
462     {
463 	if ((dirp = opendir (path)) == NULL)
464 	    /* If unable to open the directory return
465 	     * an error
466 	     */
467 	    return -1;
468 
469 	while ((dp = readdir (dirp)) != NULL)
470 	{
471 	    if (strcmp (dp->d_name, ".") == 0 ||
472 			strcmp (dp->d_name, "..") == 0)
473 		continue;
474 
475 	    sprintf (buf, "%s/%s", path, dp->d_name);
476 
477 	    if (unlink (buf) != 0 )
478 	    {
479 		if (errno == EISDIR || errno == EPERM)
480 		{
481 		    if (deep_remove_dir (buf))
482 		    {
483 			closedir (dirp);
484 			return -1;
485 		    }
486 		}
487 		else
488 		{
489 		    /* buf isn't a directory, or there are
490 		     * some sort of permision problems
491 		     */
492 		    closedir (dirp);
493 		    return -1;
494 		}
495 	    }
496 	}
497 	closedir (dirp);
498 	return rmdir (path);
499 	}
500 
501     /* Was able to remove the directory return 0 */
502     return 0;
503 }
504 
505 /* Read NCHARS bytes from descriptor FD into BUF.
506    Return the number of characters successfully read.
507    The number returned is always NCHARS unless end-of-file or error.  */
508 static size_t
509 block_read (fd, buf, nchars)
510     int fd;
511     char *buf;
512     size_t nchars;
513 {
514     char *bp = buf;
515     size_t nread;
516 
517     do
518     {
519 	nread = read (fd, bp, nchars);
520 	if (nread == (size_t)-1)
521 	{
522 #ifdef EINTR
523 	    if (errno == EINTR)
524 		continue;
525 #endif
526 	    return (size_t)-1;
527 	}
528 
529 	if (nread == 0)
530 	    break;
531 
532 	bp += nread;
533 	nchars -= nread;
534     } while (nchars != 0);
535 
536     return bp - buf;
537 }
538 
539 
540 /*
541  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
542  */
543 int
544 xcmp (file1, file2)
545     const char *file1;
546     const char *file2;
547 {
548     char *buf1, *buf2;
549     struct stat sb1, sb2;
550     int fd1, fd2;
551     int ret;
552 
553     if ((fd1 = open (file1, O_RDONLY)) < 0)
554 	error (1, errno, "cannot open file %s for comparing", file1);
555     if ((fd2 = open (file2, O_RDONLY)) < 0)
556 	error (1, errno, "cannot open file %s for comparing", file2);
557     if (fstat (fd1, &sb1) < 0)
558 	error (1, errno, "cannot fstat %s", file1);
559     if (fstat (fd2, &sb2) < 0)
560 	error (1, errno, "cannot fstat %s", file2);
561 
562     /* A generic file compare routine might compare st_dev & st_ino here
563        to see if the two files being compared are actually the same file.
564        But that won't happen in CVS, so we won't bother. */
565 
566     if (sb1.st_size != sb2.st_size)
567 	ret = 1;
568     else if (sb1.st_size == 0)
569 	ret = 0;
570     else
571     {
572 	/* FIXME: compute the optimal buffer size by computing the least
573 	   common multiple of the files st_blocks field */
574 	size_t buf_size = 8 * 1024;
575 	size_t read1;
576 	size_t read2;
577 
578 	buf1 = xmalloc (buf_size);
579 	buf2 = xmalloc (buf_size);
580 
581 	do
582 	{
583 	    read1 = block_read (fd1, buf1, buf_size);
584 	    if (read1 == (size_t)-1)
585 		error (1, errno, "cannot read file %s for comparing", file1);
586 
587 	    read2 = block_read (fd2, buf2, buf_size);
588 	    if (read2 == (size_t)-1)
589 		error (1, errno, "cannot read file %s for comparing", file2);
590 
591 	    /* assert (read1 == read2); */
592 
593 	    ret = memcmp(buf1, buf2, read1);
594 	} while (ret == 0 && read1 == buf_size);
595 
596 	free (buf1);
597 	free (buf2);
598     }
599 
600     (void) close (fd1);
601     (void) close (fd2);
602     return (ret);
603 }
604 
605 #ifdef LOSING_TMPNAM_FUNCTION
606 char *tmpnam(char *s)
607 {
608     static char value[L_tmpnam+1];
609 
610     if (s){
611        strcpy(s,"/tmp/cvsXXXXXX");
612        mktemp(s);
613        return s;
614     }else{
615        strcpy(value,"/tmp/cvsXXXXXX");
616        mktemp(s);
617        return value;
618     }
619 }
620 #endif
621 
622 /* Return non-zero iff FILENAME is absolute.
623    Trivial under Unix, but more complicated under other systems.  */
624 int
625 isabsolute (filename)
626     const char *filename;
627 {
628     return filename[0] == '/';
629 }
630 
631 
632 /* Return a pointer into PATH's last component.  */
633 char *
634 last_component (path)
635     char *path;
636 {
637     char *last = strrchr (path, '/');
638 
639     if (last)
640         return last + 1;
641     else
642         return path;
643 }
644