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