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