xref: /netbsd-src/external/gpl2/xcvs/dist/src/filesubr.c (revision 7985e76473a812187c0b7cd14e5efda1aa5c6cc9)
1a7c91847Schristos /* filesubr.c --- subroutines for dealing with files
2a7c91847Schristos    Jim Blandy <jimb@cyclic.com>
3a7c91847Schristos 
4a7c91847Schristos    This file is part of GNU CVS.
5a7c91847Schristos 
6a7c91847Schristos    GNU CVS is free software; you can redistribute it and/or modify it
7a7c91847Schristos    under the terms of the GNU General Public License as published by the
8a7c91847Schristos    Free Software Foundation; either version 2, or (at your option) any
9a7c91847Schristos    later version.
10a7c91847Schristos 
11a7c91847Schristos    This program is distributed in the hope that it will be useful,
12a7c91847Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
13a7c91847Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14a7c91847Schristos    GNU General Public License for more details.  */
155a6c14c8Schristos #include <sys/cdefs.h>
16*7985e764Schristos __RCSID("$NetBSD: filesubr.c,v 1.6 2017/09/15 21:03:26 christos Exp $");
17a7c91847Schristos 
18a7c91847Schristos /* These functions were moved out of subr.c because they need different
19a7c91847Schristos    definitions under operating systems (like, say, Windows NT) with different
20a7c91847Schristos    file system semantics.  */
21a7c91847Schristos 
22a7c91847Schristos #include "cvs.h"
23a7c91847Schristos #include "lstat.h"
24a7c91847Schristos #include "save-cwd.h"
25a7c91847Schristos #include "xsize.h"
26a7c91847Schristos 
27a7c91847Schristos static int deep_remove_dir (const char *path);
28*7985e764Schristos #ifndef S_ISBLK
29*7985e764Schristos #define S_ISBLK(a) 0
30*7985e764Schristos #endif
31*7985e764Schristos #ifndef S_ISCHR
32*7985e764Schristos #define S_ISCHR(a) 0
33*7985e764Schristos #endif
34*7985e764Schristos #define IS_DEVICE(sbp) (S_ISBLK((sbp)->st_mode) || S_ISCHR((sbp)->st_mode))
35a7c91847Schristos 
36a7c91847Schristos /*
37a7c91847Schristos  * Copies "from" to "to".
38a7c91847Schristos  */
39a7c91847Schristos void
copy_file(const char * from,const char * to)40a7c91847Schristos copy_file (const char *from, const char *to)
41a7c91847Schristos {
42a7c91847Schristos     struct stat sb;
43a7c91847Schristos     struct utimbuf t;
44a7c91847Schristos     int fdin, fdout;
45a7c91847Schristos     ssize_t rsize;
46a7c91847Schristos 
47a7c91847Schristos     TRACE (TRACE_FUNCTION, "copy(%s,%s)", from, to);
48a7c91847Schristos 
49a7c91847Schristos     if (noexec)
50a7c91847Schristos 	return;
51a7c91847Schristos 
52a7c91847Schristos     /* If the file to be copied is a link or a device, then just create
53a7c91847Schristos        the new link or device appropriately. */
54*7985e764Schristos     if ((rsize = islink (from, &sb)) > 0)
55a7c91847Schristos     {
56a7c91847Schristos 	char *source = Xreadlink (from, rsize);
579c245492Schristos 	if (symlink (source, to) == -1)
589c245492Schristos 	    error (1, errno, "cannot symlink %s to %s", source, to);
59a7c91847Schristos 	free (source);
60a7c91847Schristos 	return;
61a7c91847Schristos     }
62a7c91847Schristos 
63*7985e764Schristos     if (sb.st_ino != -1 && IS_DEVICE (&sb))
64a7c91847Schristos     {
65a7c91847Schristos #if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV)
66a7c91847Schristos 	mknod (to, sb.st_mode, sb.st_rdev);
67a7c91847Schristos #else
68a7c91847Schristos 	error (1, 0, "cannot copy device files on this system (%s)", from);
69a7c91847Schristos #endif
70a7c91847Schristos     }
71a7c91847Schristos     else
72a7c91847Schristos     {
73a7c91847Schristos 	/* Not a link or a device... probably a regular file. */
74a7c91847Schristos 	if ((fdin = open (from, O_RDONLY)) < 0)
75a7c91847Schristos 	    error (1, errno, "cannot open %s for copying", from);
76a7c91847Schristos 	if (fstat (fdin, &sb) < 0)
77a7c91847Schristos 	    error (1, errno, "cannot fstat %s", from);
78a7c91847Schristos 	if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
79a7c91847Schristos 	    error (1, errno, "cannot create %s for copying", to);
80a7c91847Schristos 	if (sb.st_size > 0)
81a7c91847Schristos 	{
82a7c91847Schristos 	    char buf[BUFSIZ];
83a7c91847Schristos 	    int n;
84a7c91847Schristos 
85a7c91847Schristos 	    for (;;)
86a7c91847Schristos 	    {
87a7c91847Schristos 		n = read (fdin, buf, sizeof(buf));
88a7c91847Schristos 		if (n == -1)
89a7c91847Schristos 		{
90a7c91847Schristos #ifdef EINTR
91a7c91847Schristos 		    if (errno == EINTR)
92a7c91847Schristos 			continue;
93a7c91847Schristos #endif
94a7c91847Schristos 		    error (1, errno, "cannot read file %s for copying", from);
95a7c91847Schristos 		}
96a7c91847Schristos 		else if (n == 0)
97a7c91847Schristos 		    break;
98a7c91847Schristos 
99a7c91847Schristos 		if (write(fdout, buf, n) != n) {
100a7c91847Schristos 		    error (1, errno, "cannot write file %s for copying", to);
101a7c91847Schristos 		}
102a7c91847Schristos 	    }
103a7c91847Schristos 	}
104a7c91847Schristos 
105a7c91847Schristos 	if (close (fdin) < 0)
106a7c91847Schristos 	    error (0, errno, "cannot close %s", from);
107a7c91847Schristos 	if (close (fdout) < 0)
108a7c91847Schristos 	    error (1, errno, "cannot close %s", to);
109a7c91847Schristos     }
110a7c91847Schristos 
111a7c91847Schristos     /* preserve last access & modification times */
112a7c91847Schristos     memset ((char *) &t, 0, sizeof (t));
113a7c91847Schristos     t.actime = sb.st_atime;
114a7c91847Schristos     t.modtime = sb.st_mtime;
115a7c91847Schristos     (void) utime (to, &t);
116a7c91847Schristos }
117a7c91847Schristos 
118a7c91847Schristos 
119a7c91847Schristos 
120a7c91847Schristos /* FIXME-krp: these functions would benefit from caching the char * &
121a7c91847Schristos    stat buf.  */
122a7c91847Schristos 
123a7c91847Schristos /*
124a7c91847Schristos  * Returns true if the argument file is a directory, or is a symbolic
125a7c91847Schristos  * link which points to a directory.
126a7c91847Schristos  */
127a7c91847Schristos bool
isdir(const char * file)128a7c91847Schristos isdir (const char *file)
129a7c91847Schristos {
130a7c91847Schristos     struct stat sb;
131a7c91847Schristos 
132a7c91847Schristos     if (stat (file, &sb) < 0)
133a7c91847Schristos 	return false;
134a7c91847Schristos     return S_ISDIR (sb.st_mode);
135a7c91847Schristos }
136a7c91847Schristos 
137a7c91847Schristos 
138a7c91847Schristos 
139a7c91847Schristos /*
140a7c91847Schristos  * Returns 0 if the argument file is not a symbolic link.
141a7c91847Schristos  * Returns size of the link if it is a symbolic link.
142a7c91847Schristos  */
143a7c91847Schristos ssize_t
islink(const char * file,struct stat * sbp)144*7985e764Schristos islink (const char *file, struct stat *sbp)
145a7c91847Schristos {
146a7c91847Schristos     ssize_t retsize = 0;
147a7c91847Schristos #ifdef S_ISLNK
148a7c91847Schristos     struct stat sb;
149*7985e764Schristos     if (sbp == NULL)
150*7985e764Schristos 	sbp = &sb;
151a7c91847Schristos 
152*7985e764Schristos     if (lstat (file, sbp) < 0) {
153*7985e764Schristos 	sbp->st_ino = -1;
154*7985e764Schristos 	return 0;
155*7985e764Schristos     }
156*7985e764Schristos     if (S_ISLNK (sbp->st_mode))
157*7985e764Schristos 	retsize = sbp->st_size;
158*7985e764Schristos #else
159*7985e764Schristos     sbp->st_ino = -1;
160a7c91847Schristos #endif
161a7c91847Schristos     return retsize;
162a7c91847Schristos }
163a7c91847Schristos 
164a7c91847Schristos 
165a7c91847Schristos 
166a7c91847Schristos /*
167a7c91847Schristos  * Returns true if the argument file is a block or
168a7c91847Schristos  * character special device.
169a7c91847Schristos  */
170a7c91847Schristos bool
isdevice(const char * file)171a7c91847Schristos isdevice (const char *file)
172a7c91847Schristos {
173a7c91847Schristos     struct stat sb;
174a7c91847Schristos 
175a7c91847Schristos     if (lstat (file, &sb) < 0)
176a7c91847Schristos 	return false;
177*7985e764Schristos     return IS_DEVICE(&sb);
178a7c91847Schristos }
179a7c91847Schristos 
180a7c91847Schristos 
181a7c91847Schristos 
182a7c91847Schristos /*
183a7c91847Schristos  * Returns true if the argument file exists.
184a7c91847Schristos  */
185a7c91847Schristos bool
isfile(const char * file)186a7c91847Schristos isfile (const char *file)
187a7c91847Schristos {
188a7c91847Schristos     return isaccessible (file, F_OK);
189a7c91847Schristos }
190a7c91847Schristos 
191274254cdSchristos #ifdef SETXID_SUPPORT
192274254cdSchristos int
ingroup(gid_t gid)193274254cdSchristos ingroup(gid_t gid)
194274254cdSchristos {
195274254cdSchristos     gid_t *gidp;
196274254cdSchristos     int i, ngroups;
197a7c91847Schristos 
198274254cdSchristos     if (gid == getegid())
199274254cdSchristos 	return 1;
200274254cdSchristos 
201274254cdSchristos     ngroups = getgroups(0, NULL);
202274254cdSchristos     if (ngroups == -1)
203274254cdSchristos 	return 0;
204274254cdSchristos 
205274254cdSchristos     if ((gidp = malloc(sizeof(gid_t) * ngroups)) == NULL)
206274254cdSchristos 	return 0;
207274254cdSchristos 
208274254cdSchristos     if (getgroups(ngroups, gidp) == -1) {
209274254cdSchristos 	free(gidp);
210274254cdSchristos 	return 0;
211274254cdSchristos     }
212274254cdSchristos 
213274254cdSchristos     for (i = 0; i < ngroups; i++)
214274254cdSchristos 	if (gid == gidp[i])
215274254cdSchristos 	    break;
216274254cdSchristos 
217274254cdSchristos     free(gidp);
218274254cdSchristos     return i != ngroups;
219274254cdSchristos }
220274254cdSchristos #endif
221a7c91847Schristos 
222a7c91847Schristos /*
223a7c91847Schristos  * Returns non-zero if the argument file is readable.
224a7c91847Schristos  */
225a7c91847Schristos bool
isreadable(const char * file)226a7c91847Schristos isreadable (const char *file)
227a7c91847Schristos {
228a7c91847Schristos     return isaccessible (file, R_OK);
229a7c91847Schristos }
230a7c91847Schristos 
231a7c91847Schristos 
232a7c91847Schristos 
233a7c91847Schristos /*
234a7c91847Schristos  * Returns non-zero if the argument file is writable.
235a7c91847Schristos  */
236a7c91847Schristos bool
iswritable(const char * file)237a7c91847Schristos iswritable (const char *file)
238a7c91847Schristos {
239a7c91847Schristos     return isaccessible (file, W_OK);
240a7c91847Schristos }
241a7c91847Schristos 
242a7c91847Schristos 
243a7c91847Schristos 
244a7c91847Schristos /*
245a7c91847Schristos  * Returns true if the argument file is accessable according to
246a7c91847Schristos  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
247a7c91847Schristos  * bits set.
248a7c91847Schristos  */
249a7c91847Schristos bool
isaccessible(const char * file,const int mode)250a7c91847Schristos isaccessible (const char *file, const int mode)
251a7c91847Schristos {
252a7c91847Schristos #ifdef SETXID_SUPPORT
253a7c91847Schristos     struct stat sb;
254a7c91847Schristos     int umask = 0;
255a7c91847Schristos     int gmask = 0;
256a7c91847Schristos     int omask = 0;
257a7c91847Schristos     int uid, mask;
258a7c91847Schristos 
259a7c91847Schristos     if (stat (file, &sb)== -1)
260a7c91847Schristos 	return false;
261a7c91847Schristos     if (mode == F_OK)
262a7c91847Schristos 	return true;
263a7c91847Schristos 
264a7c91847Schristos     uid = geteuid();
265a7c91847Schristos     if (uid == 0)		/* superuser */
266a7c91847Schristos     {
267a7c91847Schristos 	if (!(mode & X_OK) || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
268a7c91847Schristos 	    return true;
269a7c91847Schristos 
270a7c91847Schristos 	errno = EACCES;
271a7c91847Schristos 	return false;
272a7c91847Schristos     }
273a7c91847Schristos 
274a7c91847Schristos     if (mode & R_OK)
275a7c91847Schristos     {
276a7c91847Schristos 	umask |= S_IRUSR;
277a7c91847Schristos 	gmask |= S_IRGRP;
278a7c91847Schristos 	omask |= S_IROTH;
279a7c91847Schristos     }
280a7c91847Schristos     if (mode & W_OK)
281a7c91847Schristos     {
282a7c91847Schristos 	umask |= S_IWUSR;
283a7c91847Schristos 	gmask |= S_IWGRP;
284a7c91847Schristos 	omask |= S_IWOTH;
285a7c91847Schristos     }
286a7c91847Schristos     if (mode & X_OK)
287a7c91847Schristos     {
288a7c91847Schristos 	umask |= S_IXUSR;
289a7c91847Schristos 	gmask |= S_IXGRP;
290a7c91847Schristos 	omask |= S_IXOTH;
291a7c91847Schristos     }
292a7c91847Schristos 
29339ad663dSdholland     mask = sb.st_uid == uid ? umask : ingroup(sb.st_gid) ? gmask : omask;
294a7c91847Schristos     if ((sb.st_mode & mask) == mask)
295a7c91847Schristos 	return true;
296a7c91847Schristos     errno = EACCES;
297a7c91847Schristos     return false;
298a7c91847Schristos #else /* !SETXID_SUPPORT */
299a7c91847Schristos     return access (file, mode) == 0;
300a7c91847Schristos #endif /* SETXID_SUPPORT */
301a7c91847Schristos }
302a7c91847Schristos 
303a7c91847Schristos 
304a7c91847Schristos 
305a7c91847Schristos /*
306a7c91847Schristos  * Make a directory and die if it fails
307a7c91847Schristos  */
308a7c91847Schristos void
make_directory(const char * name)309a7c91847Schristos make_directory (const char *name)
310a7c91847Schristos {
311a7c91847Schristos     struct stat sb;
312a7c91847Schristos 
313a7c91847Schristos     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
314a7c91847Schristos 	    error (0, 0, "%s already exists but is not a directory", name);
315a7c91847Schristos     if (!noexec && mkdir (name, 0777) < 0)
316a7c91847Schristos 	error (1, errno, "cannot make directory %s", name);
317a7c91847Schristos }
318a7c91847Schristos 
319a7c91847Schristos /*
320a7c91847Schristos  * Make a path to the argument directory, printing a message if something
321a7c91847Schristos  * goes wrong.
322a7c91847Schristos  */
323a7c91847Schristos void
make_directories(const char * name)324a7c91847Schristos make_directories (const char *name)
325a7c91847Schristos {
326a7c91847Schristos     char *cp;
327a7c91847Schristos 
328a7c91847Schristos     if (noexec)
329a7c91847Schristos 	return;
330a7c91847Schristos 
331a7c91847Schristos     if (mkdir (name, 0777) == 0 || errno == EEXIST)
332a7c91847Schristos 	return;
333a7c91847Schristos     if (! existence_error (errno))
334a7c91847Schristos     {
335a7c91847Schristos 	error (0, errno, "cannot make path to %s", name);
336a7c91847Schristos 	return;
337a7c91847Schristos     }
338a7c91847Schristos     if ((cp = strrchr (name, '/')) == NULL)
339a7c91847Schristos 	return;
340a7c91847Schristos     *cp = '\0';
341a7c91847Schristos     make_directories (name);
342a7c91847Schristos     *cp++ = '/';
343a7c91847Schristos     if (*cp == '\0')
344a7c91847Schristos 	return;
345a7c91847Schristos     (void) mkdir (name, 0777);
346a7c91847Schristos }
347a7c91847Schristos 
348a7c91847Schristos /* Create directory NAME if it does not already exist; fatal error for
349a7c91847Schristos    other errors.  Returns 0 if directory was created; 1 if it already
350a7c91847Schristos    existed.  */
351a7c91847Schristos int
mkdir_if_needed(const char * name)352a7c91847Schristos mkdir_if_needed (const char *name)
353a7c91847Schristos {
354a7c91847Schristos     if (mkdir (name, 0777) < 0)
355a7c91847Schristos     {
356a7c91847Schristos 	int save_errno = errno;
357a7c91847Schristos 	if (save_errno != EEXIST && !isdir (name))
358a7c91847Schristos 	    error (1, save_errno, "cannot make directory %s", name);
359a7c91847Schristos 	return 1;
360a7c91847Schristos     }
361a7c91847Schristos     return 0;
362a7c91847Schristos }
363a7c91847Schristos 
364a7c91847Schristos /*
365a7c91847Schristos  * Change the mode of a file, either adding write permissions, or removing
366a7c91847Schristos  * all write permissions.  Either change honors the current umask setting.
367a7c91847Schristos  *
368a7c91847Schristos  * Don't do anything if PreservePermissions is set to `yes'.  This may
369a7c91847Schristos  * have unexpected consequences for some uses of xchmod.
370a7c91847Schristos  */
371a7c91847Schristos void
xchmod(const char * fname,int writable)372a7c91847Schristos xchmod (const char *fname, int writable)
373a7c91847Schristos {
374a7c91847Schristos     struct stat sb;
375a7c91847Schristos     mode_t mode, oumask;
376a7c91847Schristos 
377a7c91847Schristos #ifdef PRESERVE_PERMISSIONS_SUPPORT
378a7c91847Schristos     if (config->preserve_perms)
379a7c91847Schristos 	return;
380a7c91847Schristos #endif /* PRESERVE_PERMISSIONS_SUPPORT */
381a7c91847Schristos 
382a7c91847Schristos     if (stat (fname, &sb) < 0)
383a7c91847Schristos     {
384a7c91847Schristos 	if (!noexec)
385a7c91847Schristos 	    error (0, errno, "cannot stat %s", fname);
386a7c91847Schristos 	return;
387a7c91847Schristos     }
388a7c91847Schristos     oumask = umask (0);
389a7c91847Schristos     (void) umask (oumask);
390a7c91847Schristos     if (writable)
391a7c91847Schristos     {
392a7c91847Schristos 	mode = sb.st_mode | (~oumask
393a7c91847Schristos 			     & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
394a7c91847Schristos 				| ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
395a7c91847Schristos 				| ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
396a7c91847Schristos     }
397a7c91847Schristos     else
398a7c91847Schristos     {
399a7c91847Schristos 	mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
400a7c91847Schristos     }
401a7c91847Schristos 
402a7c91847Schristos     TRACE (TRACE_FUNCTION, "chmod(%s,%o)", fname, (unsigned int) mode);
403a7c91847Schristos 
404a7c91847Schristos     if (noexec)
405a7c91847Schristos 	return;
406a7c91847Schristos 
407a7c91847Schristos     if (chmod (fname, mode) < 0)
408a7c91847Schristos 	error (0, errno, "cannot change mode of file %s", fname);
409a7c91847Schristos }
410a7c91847Schristos 
411a7c91847Schristos /*
412a7c91847Schristos  * Rename a file and die if it fails
413a7c91847Schristos  */
414a7c91847Schristos void
rename_file(const char * from,const char * to)415a7c91847Schristos rename_file (const char *from, const char *to)
416a7c91847Schristos {
417a7c91847Schristos     TRACE (TRACE_FUNCTION, "rename(%s,%s)", from, to);
418a7c91847Schristos 
419a7c91847Schristos     if (noexec)
420a7c91847Schristos 	return;
421a7c91847Schristos 
422a7c91847Schristos     if (rename (from, to) < 0)
423a7c91847Schristos 	error (1, errno, "cannot rename file %s to %s", from, to);
424a7c91847Schristos }
425a7c91847Schristos 
426a7c91847Schristos /*
427a7c91847Schristos  * unlink a file, if possible.
428a7c91847Schristos  */
429a7c91847Schristos int
unlink_file(const char * f)430a7c91847Schristos unlink_file (const char *f)
431a7c91847Schristos {
432a7c91847Schristos     TRACE (TRACE_FUNCTION, "unlink_file(%s)", f);
433a7c91847Schristos 
434a7c91847Schristos     if (noexec)
435a7c91847Schristos 	return (0);
436a7c91847Schristos 
437a7c91847Schristos     return (CVS_UNLINK (f));
438a7c91847Schristos }
439a7c91847Schristos 
440a7c91847Schristos 
441a7c91847Schristos 
442a7c91847Schristos /*
443a7c91847Schristos  * Unlink a file or dir, if possible.  If it is a directory do a deep
444a7c91847Schristos  * removal of all of the files in the directory.  Return -1 on error
445a7c91847Schristos  * (in which case errno is set).
446a7c91847Schristos  */
447a7c91847Schristos int
unlink_file_dir(const char * f)448a7c91847Schristos unlink_file_dir (const char *f)
449a7c91847Schristos {
450a7c91847Schristos     struct stat sb;
451a7c91847Schristos 
452a7c91847Schristos     /* This is called by the server parent process in contexts where
453a7c91847Schristos        it is not OK to send output (e.g. after we sent "ok" to the
454a7c91847Schristos        client).  */
455a7c91847Schristos     if (!server_active)
456a7c91847Schristos 	TRACE (TRACE_FUNCTION, "unlink_file_dir(%s)", f);
457a7c91847Schristos 
458a7c91847Schristos     if (noexec)
459a7c91847Schristos 	return 0;
460a7c91847Schristos 
461a7c91847Schristos     /* For at least some unices, if root tries to unlink() a directory,
462a7c91847Schristos        instead of doing something rational like returning EISDIR,
463a7c91847Schristos        the system will gleefully go ahead and corrupt the filesystem.
464a7c91847Schristos        So we first call stat() to see if it is OK to call unlink().  This
465a7c91847Schristos        doesn't quite work--if someone creates a directory between the
466a7c91847Schristos        call to stat() and the call to unlink(), we'll still corrupt
467a7c91847Schristos        the filesystem.  Where is the Unix Haters Handbook when you need
468a7c91847Schristos        it?  */
469a7c91847Schristos     if (stat (f, &sb) < 0)
470a7c91847Schristos     {
471a7c91847Schristos 	if (existence_error (errno))
472a7c91847Schristos 	{
473a7c91847Schristos 	    /* The file or directory doesn't exist anyhow.  */
474a7c91847Schristos 	    return -1;
475a7c91847Schristos 	}
476a7c91847Schristos     }
477a7c91847Schristos     else if (S_ISDIR (sb.st_mode))
478a7c91847Schristos 	return deep_remove_dir (f);
479a7c91847Schristos 
480a7c91847Schristos     return CVS_UNLINK (f);
481a7c91847Schristos }
482a7c91847Schristos 
483a7c91847Schristos 
484a7c91847Schristos 
485a7c91847Schristos /* Remove a directory and everything it contains.  Returns 0 for
486a7c91847Schristos  * success, -1 for failure (in which case errno is set).
487a7c91847Schristos  */
488a7c91847Schristos 
489a7c91847Schristos static int
deep_remove_dir(const char * path)490a7c91847Schristos deep_remove_dir (const char *path)
491a7c91847Schristos {
492a7c91847Schristos     DIR		  *dirp;
493a7c91847Schristos     struct dirent *dp;
494a7c91847Schristos 
495a7c91847Schristos     if (rmdir (path) != 0)
496a7c91847Schristos     {
497a7c91847Schristos 	if (errno == ENOTEMPTY
498a7c91847Schristos 	    || errno == EEXIST
499a7c91847Schristos 	    /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug
500a7c91847Schristos 	       (it defines ENOTEMPTY and EEXIST to 17 but actually
501a7c91847Schristos 	       returns 87).  */
502a7c91847Schristos 	    || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87))
503a7c91847Schristos 	{
504a7c91847Schristos 	    if ((dirp = CVS_OPENDIR (path)) == NULL)
505a7c91847Schristos 		/* If unable to open the directory return
506a7c91847Schristos 		 * an error
507a7c91847Schristos 		 */
508a7c91847Schristos 		return -1;
509a7c91847Schristos 
510a7c91847Schristos 	    errno = 0;
511a7c91847Schristos 	    while ((dp = CVS_READDIR (dirp)) != NULL)
512a7c91847Schristos 	    {
513a7c91847Schristos 		char *buf;
514a7c91847Schristos 
515a7c91847Schristos 		if (strcmp (dp->d_name, ".") == 0 ||
516a7c91847Schristos 			    strcmp (dp->d_name, "..") == 0)
517a7c91847Schristos 		    continue;
518a7c91847Schristos 
519a7c91847Schristos 		buf = Xasprintf ("%s/%s", path, dp->d_name);
520a7c91847Schristos 
521a7c91847Schristos 		/* See comment in unlink_file_dir explanation of why we use
522a7c91847Schristos 		   isdir instead of just calling unlink and checking the
523a7c91847Schristos 		   status.  */
524a7c91847Schristos 		if (isdir (buf))
525a7c91847Schristos 		{
526a7c91847Schristos 		    if (deep_remove_dir (buf))
527a7c91847Schristos 		    {
528a7c91847Schristos 			CVS_CLOSEDIR (dirp);
529a7c91847Schristos 			free (buf);
530a7c91847Schristos 			return -1;
531a7c91847Schristos 		    }
532a7c91847Schristos 		}
533a7c91847Schristos 		else
534a7c91847Schristos 		{
535a7c91847Schristos 		    if (CVS_UNLINK (buf) != 0)
536a7c91847Schristos 		    {
537a7c91847Schristos 			CVS_CLOSEDIR (dirp);
538a7c91847Schristos 			free (buf);
539a7c91847Schristos 			return -1;
540a7c91847Schristos 		    }
541a7c91847Schristos 		}
542a7c91847Schristos 		free (buf);
543a7c91847Schristos 
544a7c91847Schristos 		errno = 0;
545a7c91847Schristos 	    }
546a7c91847Schristos 	    if (errno != 0)
547a7c91847Schristos 	    {
548a7c91847Schristos 		int save_errno = errno;
549a7c91847Schristos 		CVS_CLOSEDIR (dirp);
550a7c91847Schristos 		errno = save_errno;
551a7c91847Schristos 		return -1;
552a7c91847Schristos 	    }
553a7c91847Schristos 	    CVS_CLOSEDIR (dirp);
554a7c91847Schristos 	    return rmdir (path);
555a7c91847Schristos 	}
556a7c91847Schristos 	else
557a7c91847Schristos 	    return -1;
558a7c91847Schristos     }
559a7c91847Schristos 
560a7c91847Schristos     /* Was able to remove the directory return 0 */
561a7c91847Schristos     return 0;
562a7c91847Schristos }
563a7c91847Schristos 
564a7c91847Schristos 
565a7c91847Schristos 
566a7c91847Schristos /* Read NCHARS bytes from descriptor FD into BUF.
567a7c91847Schristos    Return the number of characters successfully read.
568a7c91847Schristos    The number returned is always NCHARS unless end-of-file or error.  */
569a7c91847Schristos static size_t
block_read(int fd,char * buf,size_t nchars)570a7c91847Schristos block_read (int fd, char *buf, size_t nchars)
571a7c91847Schristos {
572a7c91847Schristos     char *bp = buf;
573a7c91847Schristos     size_t nread;
574a7c91847Schristos 
575a7c91847Schristos     do
576a7c91847Schristos     {
577a7c91847Schristos 	nread = read (fd, bp, nchars);
578a7c91847Schristos 	if (nread == (size_t)-1)
579a7c91847Schristos 	{
580a7c91847Schristos #ifdef EINTR
581a7c91847Schristos 	    if (errno == EINTR)
582a7c91847Schristos 		continue;
583a7c91847Schristos #endif
584a7c91847Schristos 	    return (size_t)-1;
585a7c91847Schristos 	}
586a7c91847Schristos 
587a7c91847Schristos 	if (nread == 0)
588a7c91847Schristos 	    break;
589a7c91847Schristos 
590a7c91847Schristos 	bp += nread;
591a7c91847Schristos 	nchars -= nread;
592a7c91847Schristos     } while (nchars != 0);
593a7c91847Schristos 
594a7c91847Schristos     return bp - buf;
595a7c91847Schristos }
596a7c91847Schristos 
597a7c91847Schristos 
598a7c91847Schristos /*
599a7c91847Schristos  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
600a7c91847Schristos  * If FILE1 and FILE2 are special files, compare their salient characteristics
601a7c91847Schristos  * (i.e. major/minor device numbers, links, etc.
602a7c91847Schristos  */
603a7c91847Schristos int
xcmp(const char * file1,const char * file2)604a7c91847Schristos xcmp (const char *file1, const char *file2)
605a7c91847Schristos {
606a7c91847Schristos     char *buf1, *buf2;
607a7c91847Schristos     struct stat sb1, sb2;
608a7c91847Schristos     int fd1, fd2;
609a7c91847Schristos     int ret;
610a7c91847Schristos 
611a7c91847Schristos     if (lstat (file1, &sb1) < 0)
612a7c91847Schristos 	error (1, errno, "cannot lstat %s", file1);
613a7c91847Schristos     if (lstat (file2, &sb2) < 0)
614a7c91847Schristos 	error (1, errno, "cannot lstat %s", file2);
615a7c91847Schristos 
616a7c91847Schristos     /* If FILE1 and FILE2 are not the same file type, they are unequal. */
617a7c91847Schristos     if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT))
618a7c91847Schristos 	return 1;
619a7c91847Schristos 
620a7c91847Schristos     /* If FILE1 and FILE2 are symlinks, they are equal if they point to
621a7c91847Schristos        the same thing. */
622a7c91847Schristos #ifdef S_ISLNK
623a7c91847Schristos     if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode))
624a7c91847Schristos     {
625a7c91847Schristos 	int result;
626a7c91847Schristos 	buf1 = Xreadlink (file1, sb1.st_size);
627a7c91847Schristos 	buf2 = Xreadlink (file2, sb2.st_size);
628a7c91847Schristos 	result = (strcmp (buf1, buf2) == 0);
629a7c91847Schristos 	free (buf1);
630a7c91847Schristos 	free (buf2);
631a7c91847Schristos 	return result;
632a7c91847Schristos     }
633a7c91847Schristos #endif
634a7c91847Schristos 
635a7c91847Schristos     /* If FILE1 and FILE2 are devices, they are equal if their device
636a7c91847Schristos        numbers match. */
637a7c91847Schristos     if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode))
638a7c91847Schristos     {
639a7c91847Schristos #ifdef HAVE_STRUCT_STAT_ST_RDEV
640a7c91847Schristos 	if (sb1.st_rdev == sb2.st_rdev)
641a7c91847Schristos 	    return 0;
642a7c91847Schristos 	else
643a7c91847Schristos 	    return 1;
644a7c91847Schristos #else
645a7c91847Schristos 	error (1, 0, "cannot compare device files on this system (%s and %s)",
646a7c91847Schristos 	       file1, file2);
647a7c91847Schristos #endif
648a7c91847Schristos     }
649a7c91847Schristos 
650a7c91847Schristos     if ((fd1 = open (file1, O_RDONLY)) < 0)
651a7c91847Schristos 	error (1, errno, "cannot open file %s for comparing", file1);
652a7c91847Schristos     if ((fd2 = open (file2, O_RDONLY)) < 0)
653a7c91847Schristos 	error (1, errno, "cannot open file %s for comparing", file2);
654a7c91847Schristos 
655a7c91847Schristos     /* A generic file compare routine might compare st_dev & st_ino here
656a7c91847Schristos        to see if the two files being compared are actually the same file.
657a7c91847Schristos        But that won't happen in CVS, so we won't bother. */
658a7c91847Schristos 
659a7c91847Schristos     if (sb1.st_size != sb2.st_size)
660a7c91847Schristos 	ret = 1;
661a7c91847Schristos     else if (sb1.st_size == 0)
662a7c91847Schristos 	ret = 0;
663a7c91847Schristos     else
664a7c91847Schristos     {
665a7c91847Schristos 	/* FIXME: compute the optimal buffer size by computing the least
666a7c91847Schristos 	   common multiple of the files st_blocks field */
667a7c91847Schristos 	size_t buf_size = 8 * 1024;
668a7c91847Schristos 	size_t read1;
669a7c91847Schristos 	size_t read2;
670a7c91847Schristos 
671a7c91847Schristos 	buf1 = xmalloc (buf_size);
672a7c91847Schristos 	buf2 = xmalloc (buf_size);
673a7c91847Schristos 
674a7c91847Schristos 	do
675a7c91847Schristos 	{
676a7c91847Schristos 	    read1 = block_read (fd1, buf1, buf_size);
677a7c91847Schristos 	    if (read1 == (size_t)-1)
678a7c91847Schristos 		error (1, errno, "cannot read file %s for comparing", file1);
679a7c91847Schristos 
680a7c91847Schristos 	    read2 = block_read (fd2, buf2, buf_size);
681a7c91847Schristos 	    if (read2 == (size_t)-1)
682a7c91847Schristos 		error (1, errno, "cannot read file %s for comparing", file2);
683a7c91847Schristos 
684a7c91847Schristos 	    /* assert (read1 == read2); */
685a7c91847Schristos 
686a7c91847Schristos 	    ret = memcmp(buf1, buf2, read1);
687a7c91847Schristos 	} while (ret == 0 && read1 == buf_size);
688a7c91847Schristos 
689a7c91847Schristos 	free (buf1);
690a7c91847Schristos 	free (buf2);
691a7c91847Schristos     }
692a7c91847Schristos 
693a7c91847Schristos     (void) close (fd1);
694a7c91847Schristos     (void) close (fd2);
695a7c91847Schristos     return (ret);
696a7c91847Schristos }
697a7c91847Schristos 
698a7c91847Schristos /* Generate a unique temporary filename.  Returns a pointer to a newly
699a7c91847Schristos  * malloc'd string containing the name.  Returns successfully or not at
700a7c91847Schristos  * all.
701a7c91847Schristos  *
702a7c91847Schristos  *     THIS FUNCTION IS DEPRECATED!!!  USE cvs_temp_file INSTEAD!!!
703a7c91847Schristos  *
704a7c91847Schristos  * and yes, I know about the way the rcs commands use temp files.  I think
705a7c91847Schristos  * they should be converted too but I don't have time to look into it right
706a7c91847Schristos  * now.
707a7c91847Schristos  */
708a7c91847Schristos char *
cvs_temp_name(void)709a7c91847Schristos cvs_temp_name (void)
710a7c91847Schristos {
711a7c91847Schristos     char *fn;
712a7c91847Schristos     FILE *fp;
713a7c91847Schristos 
714a7c91847Schristos     fp = cvs_temp_file (&fn);
715a7c91847Schristos     if (fp == NULL)
716a7c91847Schristos 	error (1, errno, "Failed to create temporary file");
717a7c91847Schristos     if (fclose (fp) == EOF)
718a7c91847Schristos 	error (0, errno, "Failed to close temporary file %s", fn);
719a7c91847Schristos     return fn;
720a7c91847Schristos }
721a7c91847Schristos 
722a7c91847Schristos /* Generate a unique temporary filename and return an open file stream
723a7c91847Schristos  * to the truncated file by that name
724a7c91847Schristos  *
725a7c91847Schristos  *  INPUTS
726a7c91847Schristos  *	filename	where to place the pointer to the newly allocated file
727a7c91847Schristos  *   			name string
728a7c91847Schristos  *
729a7c91847Schristos  *  OUTPUTS
730a7c91847Schristos  *	filename	dereferenced, will point to the newly allocated file
731a7c91847Schristos  *			name string.  This value is undefined if the function
732a7c91847Schristos  *			returns an error.
733a7c91847Schristos  *
734a7c91847Schristos  *  RETURNS
735a7c91847Schristos  *	An open file pointer to a read/write mode empty temporary file with the
736a7c91847Schristos  *	unique file name or NULL on failure.
737a7c91847Schristos  *
738a7c91847Schristos  *  ERRORS
739a7c91847Schristos  *	On error, errno will be set to some value either by CVS_FOPEN or
740a7c91847Schristos  *	whatever system function is called to generate the temporary file name.
741a7c91847Schristos  *	The value of filename is undefined on error.
742a7c91847Schristos  */
743a7c91847Schristos FILE *
cvs_temp_file(char ** filename)744a7c91847Schristos cvs_temp_file (char **filename)
745a7c91847Schristos {
746a7c91847Schristos     char *fn;
747a7c91847Schristos     FILE *fp;
748a7c91847Schristos     int fd;
749a7c91847Schristos 
750a7c91847Schristos     /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
751a7c91847Schristos      * some of the rcs & diff functions which rely on a temp file run in
752a7c91847Schristos      * noexec mode too.
753a7c91847Schristos      */
754a7c91847Schristos 
755a7c91847Schristos     assert (filename != NULL);
756a7c91847Schristos 
757a7c91847Schristos     fn = Xasprintf ("%s/%s", get_cvs_tmp_dir (), "cvsXXXXXX");
758a7c91847Schristos     fd = mkstemp (fn);
759a7c91847Schristos 
760a7c91847Schristos     /* a NULL return will be interpreted by callers as an error and
761a7c91847Schristos      * errno should still be set
762a7c91847Schristos      */
763a7c91847Schristos     if (fd == -1)
764a7c91847Schristos 	fp = NULL;
765a7c91847Schristos     else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
766a7c91847Schristos     {
767a7c91847Schristos 	/* Attempt to close and unlink the file since mkstemp returned
768a7c91847Schristos 	 * sucessfully and we believe it's been created and opened.
769a7c91847Schristos 	 */
770a7c91847Schristos  	int save_errno = errno;
771a7c91847Schristos 	if (close (fd))
772a7c91847Schristos 	    error (0, errno, "Failed to close temporary file %s", fn);
773a7c91847Schristos 	if (CVS_UNLINK (fn))
774a7c91847Schristos 	    error (0, errno, "Failed to unlink temporary file %s", fn);
775a7c91847Schristos 	errno = save_errno;
776a7c91847Schristos     }
777a7c91847Schristos 
778a7c91847Schristos     if (fp == NULL)
779a7c91847Schristos 	free (fn);
780a7c91847Schristos 
781a7c91847Schristos     /* mkstemp is defined to open mode 0600 using glibc 2.0.7+.  There used
782a7c91847Schristos      * to be a complicated #ifdef checking the library versions here and then
783a7c91847Schristos      * a chmod 0600 on the temp file for versions of glibc less than 2.1.  This
784a7c91847Schristos      * is rather a special case, leaves a race condition open regardless, and
785a7c91847Schristos      * one could hope that sysadmins have read the relevant security
786a7c91847Schristos      * announcements and upgraded by now to a version with a fix committed in
787a7c91847Schristos      * January of 1999.
788a7c91847Schristos      *
789a7c91847Schristos      * If it is decided at some point that old, buggy versions of glibc should
790a7c91847Schristos      * still be catered to, a umask of 0600 should be set before file creation
791a7c91847Schristos      * instead then reset after file creation since this would avoid the race
792a7c91847Schristos      * condition that the chmod left open to exploitation.
793a7c91847Schristos      */
794a7c91847Schristos 
795a7c91847Schristos     *filename = fn;
796a7c91847Schristos     return fp;
797a7c91847Schristos }
798a7c91847Schristos 
799a7c91847Schristos 
800a7c91847Schristos 
801a7c91847Schristos /* Return a pointer into PATH's last component.  */
802a7c91847Schristos const char *
last_component(const char * path)803a7c91847Schristos last_component (const char *path)
804a7c91847Schristos {
805a7c91847Schristos     const char *last = strrchr (path, '/');
806a7c91847Schristos 
807a7c91847Schristos     if (last && (last != path))
808a7c91847Schristos         return last + 1;
809a7c91847Schristos     else
810a7c91847Schristos         return path;
811a7c91847Schristos }
812a7c91847Schristos 
813a7c91847Schristos 
814a7c91847Schristos 
815a7c91847Schristos /* Return the home directory.  Returns a pointer to storage
816a7c91847Schristos    managed by this function or its callees (currently getenv).
817a7c91847Schristos    This function will return the same thing every time it is
818a7c91847Schristos    called.  Returns NULL if there is no home directory.
819a7c91847Schristos 
820a7c91847Schristos    Note that for a pserver server, this may return root's home
821a7c91847Schristos    directory.  What typically happens is that upon being started from
822a7c91847Schristos    inetd, before switching users, the code in cvsrc.c calls
823a7c91847Schristos    get_homedir which remembers root's home directory in the static
824a7c91847Schristos    variable.  Then the switch happens and get_homedir might return a
825a7c91847Schristos    directory that we don't even have read or execute permissions for
826a7c91847Schristos    (which is bad, when various parts of CVS try to read there).  One
827a7c91847Schristos    fix would be to make the value returned by get_homedir only good
828a7c91847Schristos    until the next call (which would free the old value).  Another fix
829a7c91847Schristos    would be to just always malloc our answer, and let the caller free
830a7c91847Schristos    it (that is best, because some day we may need to be reentrant).
831a7c91847Schristos 
832a7c91847Schristos    The workaround is to put -f in inetd.conf which means that
833a7c91847Schristos    get_homedir won't get called until after the switch in user ID.
834a7c91847Schristos 
835a7c91847Schristos    The whole concept of a "home directory" on the server is pretty
836a7c91847Schristos    iffy, although I suppose some people probably are relying on it for
837a7c91847Schristos    .cvsrc and such, in the cases where it works.  */
838a7c91847Schristos char *
get_homedir(void)839a7c91847Schristos get_homedir (void)
840a7c91847Schristos {
841a7c91847Schristos     static char *home = NULL;
842a7c91847Schristos     char *env;
843a7c91847Schristos     struct passwd *pw;
844a7c91847Schristos 
845a7c91847Schristos     if (home != NULL)
846a7c91847Schristos 	return home;
847a7c91847Schristos 
848a7c91847Schristos     if (!server_active && (env = getenv ("HOME")) != NULL)
849a7c91847Schristos 	home = env;
850a7c91847Schristos     else if ((pw = (struct passwd *) getpwuid (getuid ()))
851a7c91847Schristos 	     && pw->pw_dir)
852a7c91847Schristos 	home = xstrdup (pw->pw_dir);
853a7c91847Schristos     else
854a7c91847Schristos 	return 0;
855a7c91847Schristos 
856a7c91847Schristos     return home;
857a7c91847Schristos }
858a7c91847Schristos 
859a7c91847Schristos /* Compose a path to a file in the home directory.  This is necessary because
860a7c91847Schristos  * of different behavior on UNIX and VMS.  See the notes in vms/filesubr.c.
861a7c91847Schristos  *
862a7c91847Schristos  * A more clean solution would be something more along the lines of a
863a7c91847Schristos  * "join a directory to a filename" kind of thing which was not specific to
864a7c91847Schristos  * the homedir.  This should aid portability between UNIX, Mac, Windows, VMS,
865a7c91847Schristos  * and possibly others.  This is already handled by Perl - it might be
866a7c91847Schristos  * interesting to see how much of the code was written in C since Perl is under
867a7c91847Schristos  * the GPL and the Artistic license - we might be able to use it.
868a7c91847Schristos  */
869a7c91847Schristos char *
strcat_filename_onto_homedir(const char * dir,const char * file)870a7c91847Schristos strcat_filename_onto_homedir (const char *dir, const char *file)
871a7c91847Schristos {
872a7c91847Schristos     char *path = Xasprintf ("%s/%s", dir, file);
873a7c91847Schristos     return path;
874a7c91847Schristos }
875a7c91847Schristos 
876a7c91847Schristos /* See cvs.h for description.  On unix this does nothing, because the
877a7c91847Schristos    shell expands the wildcards.  */
878a7c91847Schristos void
expand_wild(int argc,char ** argv,int * pargc,char *** pargv)879a7c91847Schristos expand_wild (int argc, char **argv, int *pargc, char ***pargv)
880a7c91847Schristos {
881a7c91847Schristos     int i;
882a7c91847Schristos     if (size_overflow_p (xtimes (argc, sizeof (char *)))) {
883a7c91847Schristos 	*pargc = 0;
884a7c91847Schristos 	*pargv = NULL;
885a7c91847Schristos 	error (0, 0, "expand_wild: too many arguments");
886a7c91847Schristos 	return;
887a7c91847Schristos     }
888a7c91847Schristos     *pargc = argc;
889a7c91847Schristos     *pargv = xnmalloc (argc, sizeof (char *));
890a7c91847Schristos     for (i = 0; i < argc; ++i)
891a7c91847Schristos 	(*pargv)[i] = xstrdup (argv[i]);
892a7c91847Schristos }
893a7c91847Schristos 
894a7c91847Schristos 
895a7c91847Schristos 
896a7c91847Schristos static char *tmpdir_env;
897a7c91847Schristos 
898a7c91847Schristos /* Return path to temp directory.
899a7c91847Schristos  */
900a7c91847Schristos const char *
get_system_temp_dir(void)901a7c91847Schristos get_system_temp_dir (void)
902a7c91847Schristos {
903a7c91847Schristos     if (!tmpdir_env) tmpdir_env = getenv (TMPDIR_ENV);
904a7c91847Schristos     return tmpdir_env;
905a7c91847Schristos }
906a7c91847Schristos 
907a7c91847Schristos 
908a7c91847Schristos 
909a7c91847Schristos void
push_env_temp_dir(void)910a7c91847Schristos push_env_temp_dir (void)
911a7c91847Schristos {
912a7c91847Schristos     const char *tmpdir = get_cvs_tmp_dir ();
913a7c91847Schristos     if (tmpdir_env && strcmp (tmpdir_env, tmpdir))
914a7c91847Schristos 	setenv (TMPDIR_ENV, tmpdir, 1);
915a7c91847Schristos }
916