117721Speter/* filesubr.c --- subroutines for dealing with files
217721Speter   Jim Blandy <jimb@cyclic.com>
317721Speter
417721Speter   This file is part of GNU CVS.
517721Speter
617721Speter   GNU CVS is free software; you can redistribute it and/or modify it
717721Speter   under the terms of the GNU General Public License as published by the
817721Speter   Free Software Foundation; either version 2, or (at your option) any
917721Speter   later version.
1017721Speter
1117721Speter   This program is distributed in the hope that it will be useful,
1217721Speter   but WITHOUT ANY WARRANTY; without even the implied warranty of
1317721Speter   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1425839Speter   GNU General Public License for more details.  */
1517721Speter
1617721Speter/* These functions were moved out of subr.c because they need different
1717721Speter   definitions under operating systems (like, say, Windows NT) with different
1817721Speter   file system semantics.  */
1917721Speter
2081407Speter#include <assert.h>
2117721Speter#include "cvs.h"
2217721Speter
23175269Sobrien#include "xsize.h"
24175269Sobrien
2517721Speterstatic int deep_remove_dir PROTO((const char *path));
2617721Speter
2717721Speter/*
2817721Speter * Copies "from" to "to".
2917721Speter */
3017721Spetervoid
3117721Spetercopy_file (from, to)
3217721Speter    const char *from;
3317721Speter    const char *to;
3417721Speter{
3517721Speter    struct stat sb;
3617721Speter    struct utimbuf t;
3717721Speter    int fdin, fdout;
3817721Speter
3917721Speter    if (trace)
4054431Speter	(void) fprintf (stderr, "%s-> copy(%s,%s)\n",
4154431Speter			CLIENT_SERVER_STR, from, to);
4217721Speter    if (noexec)
4317721Speter	return;
4417721Speter
4534461Speter    /* If the file to be copied is a link or a device, then just create
4634461Speter       the new link or device appropriately. */
4734461Speter    if (islink (from))
4817721Speter    {
4934461Speter	char *source = xreadlink (from);
5034461Speter	symlink (source, to);
5134461Speter	free (source);
5234461Speter	return;
5334461Speter    }
5417721Speter
5534461Speter    if (isdevice (from))
5634461Speter    {
57107487Speter#if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV)
5834461Speter	if (stat (from, &sb) < 0)
5934461Speter	    error (1, errno, "cannot stat %s", from);
6034461Speter	mknod (to, sb.st_mode, sb.st_rdev);
6166528Speter#else
6266528Speter	error (1, 0, "cannot copy device files on this system (%s)", from);
6366528Speter#endif
6434461Speter    }
6534461Speter    else
6634461Speter    {
6734461Speter	/* Not a link or a device... probably a regular file. */
6834461Speter	if ((fdin = open (from, O_RDONLY)) < 0)
6934461Speter	    error (1, errno, "cannot open %s for copying", from);
7034461Speter	if (fstat (fdin, &sb) < 0)
7134461Speter	    error (1, errno, "cannot fstat %s", from);
7234461Speter	if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
7334461Speter	    error (1, errno, "cannot create %s for copying", to);
7434461Speter	if (sb.st_size > 0)
7517721Speter	{
7634461Speter	    char buf[BUFSIZ];
7734461Speter	    int n;
7834461Speter
7934461Speter	    for (;;)
8017721Speter	    {
8134461Speter		n = read (fdin, buf, sizeof(buf));
8234461Speter		if (n == -1)
8334461Speter		{
8417721Speter#ifdef EINTR
8534461Speter		    if (errno == EINTR)
8634461Speter			continue;
8717721Speter#endif
8834461Speter		    error (1, errno, "cannot read file %s for copying", from);
8934461Speter		}
9034461Speter		else if (n == 0)
9134461Speter		    break;
9234461Speter
9334461Speter		if (write(fdout, buf, n) != n) {
9434461Speter		    error (1, errno, "cannot write file %s for copying", to);
9534461Speter		}
9617721Speter	    }
9717721Speter
9817721Speter#ifdef HAVE_FSYNC
9934461Speter	    if (fsync (fdout))
10034461Speter		error (1, errno, "cannot fsync file %s after copying", to);
10117721Speter#endif
10234461Speter	}
10334461Speter
10434461Speter	if (close (fdin) < 0)
10534461Speter	    error (0, errno, "cannot close %s", from);
10634461Speter	if (close (fdout) < 0)
10734461Speter	    error (1, errno, "cannot close %s", to);
10817721Speter    }
10917721Speter
110175269Sobrien    /* preserve last access & modification times */
11117721Speter    memset ((char *) &t, 0, sizeof (t));
11217721Speter    t.actime = sb.st_atime;
11317721Speter    t.modtime = sb.st_mtime;
11417721Speter    (void) utime (to, &t);
11517721Speter}
11617721Speter
11717721Speter/* FIXME-krp: these functions would benefit from caching the char * &
11817721Speter   stat buf.  */
11917721Speter
12017721Speter/*
12117721Speter * Returns non-zero if the argument file is a directory, or is a symbolic
12217721Speter * link which points to a directory.
12317721Speter */
12417721Speterint
12517721Speterisdir (file)
12617721Speter    const char *file;
12717721Speter{
12817721Speter    struct stat sb;
12917721Speter
13017721Speter    if (stat (file, &sb) < 0)
13117721Speter	return (0);
13217721Speter    return (S_ISDIR (sb.st_mode));
13317721Speter}
13417721Speter
13517721Speter/*
13617721Speter * Returns non-zero if the argument file is a symbolic link.
13717721Speter */
13817721Speterint
13917721Speterislink (file)
14017721Speter    const char *file;
14117721Speter{
14217721Speter#ifdef S_ISLNK
14317721Speter    struct stat sb;
14417721Speter
14534461Speter    if (CVS_LSTAT (file, &sb) < 0)
14617721Speter	return (0);
14717721Speter    return (S_ISLNK (sb.st_mode));
14817721Speter#else
14917721Speter    return (0);
15017721Speter#endif
15117721Speter}
15217721Speter
15317721Speter/*
15434461Speter * Returns non-zero if the argument file is a block or
15534461Speter * character special device.
15634461Speter */
15734461Speterint
15834461Speterisdevice (file)
15934461Speter    const char *file;
16034461Speter{
16134461Speter    struct stat sb;
16234461Speter
16334461Speter    if (CVS_LSTAT (file, &sb) < 0)
16434461Speter	return (0);
16534461Speter#ifdef S_ISBLK
16634461Speter    if (S_ISBLK (sb.st_mode))
16734461Speter	return 1;
16834461Speter#endif
16934461Speter#ifdef S_ISCHR
17034461Speter    if (S_ISCHR (sb.st_mode))
17134461Speter	return 1;
17234461Speter#endif
17334461Speter    return 0;
17434461Speter}
17534461Speter
17634461Speter/*
17717721Speter * Returns non-zero if the argument file exists.
17817721Speter */
17917721Speterint
18017721Speterisfile (file)
18117721Speter    const char *file;
18217721Speter{
18317721Speter    return isaccessible(file, F_OK);
18417721Speter}
18517721Speter
18617721Speter/*
18717721Speter * Returns non-zero if the argument file is readable.
18817721Speter */
18917721Speterint
19017721Speterisreadable (file)
19117721Speter    const char *file;
19217721Speter{
19317721Speter    return isaccessible(file, R_OK);
19417721Speter}
19517721Speter
19617721Speter/*
19717721Speter * Returns non-zero if the argument file is writable.
19817721Speter */
19917721Speterint
20017721Speteriswritable (file)
20117721Speter    const char *file;
20217721Speter{
20317721Speter    return isaccessible(file, W_OK);
20417721Speter}
20517721Speter
20617721Speter/*
20717721Speter * Returns non-zero if the argument file is accessable according to
20817721Speter * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
20917721Speter * bits set.
21017721Speter */
21117721Speterint
21217721Speterisaccessible (file, mode)
21317721Speter    const char *file;
21417721Speter    const int mode;
21517721Speter{
21617721Speter#ifdef SETXID_SUPPORT
21717721Speter    struct stat sb;
21817721Speter    int umask = 0;
21917721Speter    int gmask = 0;
22017721Speter    int omask = 0;
221107487Speter    int uid, mask;
22217721Speter
22317721Speter    if (stat(file, &sb) == -1)
22417721Speter	return 0;
22517721Speter    if (mode == F_OK)
22617721Speter	return 1;
22717721Speter
22817721Speter    uid = geteuid();
22917721Speter    if (uid == 0)		/* superuser */
23017721Speter    {
231107487Speter	if (!(mode & X_OK) || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
23217721Speter	    return 1;
233107487Speter
234107487Speter	errno = EACCES;
235107487Speter	return 0;
23617721Speter    }
23717721Speter
23817721Speter    if (mode & R_OK)
23917721Speter    {
24017721Speter	umask |= S_IRUSR;
24117721Speter	gmask |= S_IRGRP;
24217721Speter	omask |= S_IROTH;
24317721Speter    }
24417721Speter    if (mode & W_OK)
24517721Speter    {
24617721Speter	umask |= S_IWUSR;
24717721Speter	gmask |= S_IWGRP;
24817721Speter	omask |= S_IWOTH;
24917721Speter    }
25017721Speter    if (mode & X_OK)
25117721Speter    {
25217721Speter	umask |= S_IXUSR;
25317721Speter	gmask |= S_IXGRP;
25417721Speter	omask |= S_IXOTH;
25517721Speter    }
25617721Speter
257107487Speter    mask = sb.st_uid == uid ? umask : sb.st_gid == getegid() ? gmask : omask;
258107487Speter    if ((sb.st_mode & mask) == mask)
259107487Speter	return 1;
260107487Speter    errno = EACCES;
261107487Speter    return 0;
26217721Speter#else
26317721Speter    return access(file, mode) == 0;
26417721Speter#endif
26517721Speter}
26617721Speter
26717721Speter/*
26817721Speter * Open a file and die if it fails
26917721Speter */
27017721SpeterFILE *
27117721Speteropen_file (name, mode)
27217721Speter    const char *name;
27317721Speter    const char *mode;
27417721Speter{
27517721Speter    FILE *fp;
27617721Speter
27717721Speter    if ((fp = fopen (name, mode)) == NULL)
27817721Speter	error (1, errno, "cannot open %s", name);
27917721Speter    return (fp);
28017721Speter}
28117721Speter
28217721Speter/*
28317721Speter * Make a directory and die if it fails
28417721Speter */
28517721Spetervoid
28617721Spetermake_directory (name)
28717721Speter    const char *name;
28817721Speter{
28917721Speter    struct stat sb;
29017721Speter
29117721Speter    if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
29217721Speter	    error (0, 0, "%s already exists but is not a directory", name);
29317721Speter    if (!noexec && mkdir (name, 0777) < 0)
29417721Speter	error (1, errno, "cannot make directory %s", name);
29517721Speter}
29617721Speter
29717721Speter/*
29817721Speter * Make a path to the argument directory, printing a message if something
29917721Speter * goes wrong.
30017721Speter */
30117721Spetervoid
30217721Spetermake_directories (name)
30317721Speter    const char *name;
30417721Speter{
30517721Speter    char *cp;
30617721Speter
30717721Speter    if (noexec)
30817721Speter	return;
30917721Speter
31017721Speter    if (mkdir (name, 0777) == 0 || errno == EEXIST)
31117721Speter	return;
31217721Speter    if (! existence_error (errno))
31317721Speter    {
31417721Speter	error (0, errno, "cannot make path to %s", name);
31517721Speter	return;
31617721Speter    }
31717721Speter    if ((cp = strrchr (name, '/')) == NULL)
31817721Speter	return;
31917721Speter    *cp = '\0';
32017721Speter    make_directories (name);
32117721Speter    *cp++ = '/';
32217721Speter    if (*cp == '\0')
32317721Speter	return;
32417721Speter    (void) mkdir (name, 0777);
32517721Speter}
32617721Speter
32725839Speter/* Create directory NAME if it does not already exist; fatal error for
32825839Speter   other errors.  Returns 0 if directory was created; 1 if it already
32925839Speter   existed.  */
33025839Speterint
33125839Spetermkdir_if_needed (name)
332128269Speter    const char *name;
33325839Speter{
33425839Speter    if (mkdir (name, 0777) < 0)
33525839Speter    {
336128269Speter	int save_errno = errno;
337128269Speter	if (save_errno != EEXIST && !isdir (name))
338128269Speter	    error (1, save_errno, "cannot make directory %s", name);
33925839Speter	return 1;
34025839Speter    }
34125839Speter    return 0;
34225839Speter}
34325839Speter
34417721Speter/*
34517721Speter * Change the mode of a file, either adding write permissions, or removing
34617721Speter * all write permissions.  Either change honors the current umask setting.
34734461Speter *
34834461Speter * Don't do anything if PreservePermissions is set to `yes'.  This may
34934461Speter * have unexpected consequences for some uses of xchmod.
35017721Speter */
35117721Spetervoid
35217721Speterxchmod (fname, writable)
353128269Speter    const char *fname;
35417721Speter    int writable;
35517721Speter{
35617721Speter    struct stat sb;
35717721Speter    mode_t mode, oumask;
35817721Speter
35934461Speter    if (preserve_perms)
36034461Speter	return;
36134461Speter
36217721Speter    if (stat (fname, &sb) < 0)
36317721Speter    {
36417721Speter	if (!noexec)
36517721Speter	    error (0, errno, "cannot stat %s", fname);
36617721Speter	return;
36717721Speter    }
36817721Speter    oumask = umask (0);
36917721Speter    (void) umask (oumask);
37017721Speter    if (writable)
37117721Speter    {
37217721Speter	mode = sb.st_mode | (~oumask
37317721Speter			     & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
37417721Speter				| ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
37517721Speter				| ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
37617721Speter    }
37717721Speter    else
37817721Speter    {
37917721Speter	mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
38017721Speter    }
38117721Speter
38217721Speter    if (trace)
38354431Speter	(void) fprintf (stderr, "%s-> chmod(%s,%o)\n",
38454431Speter			CLIENT_SERVER_STR, fname,
38517721Speter			(unsigned int) mode);
38617721Speter    if (noexec)
38717721Speter	return;
38817721Speter
38917721Speter    if (chmod (fname, mode) < 0)
39017721Speter	error (0, errno, "cannot change mode of file %s", fname);
39117721Speter}
39217721Speter
39317721Speter/*
39417721Speter * Rename a file and die if it fails
39517721Speter */
39617721Spetervoid
39717721Speterrename_file (from, to)
39817721Speter    const char *from;
39917721Speter    const char *to;
40017721Speter{
40117721Speter    if (trace)
40254431Speter	(void) fprintf (stderr, "%s-> rename(%s,%s)\n",
40354431Speter			CLIENT_SERVER_STR, from, to);
40417721Speter    if (noexec)
40517721Speter	return;
40617721Speter
40717721Speter    if (rename (from, to) < 0)
40817721Speter	error (1, errno, "cannot rename file %s to %s", from, to);
40917721Speter}
41017721Speter
41117721Speter/*
41217721Speter * unlink a file, if possible.
41317721Speter */
41417721Speterint
41517721Speterunlink_file (f)
41617721Speter    const char *f;
41717721Speter{
41817721Speter    if (trace)
41981407Speter	(void) fprintf (stderr, "%s-> unlink_file(%s)\n",
42054431Speter			CLIENT_SERVER_STR, f);
42117721Speter    if (noexec)
42217721Speter	return (0);
42317721Speter
42481407Speter    return (CVS_UNLINK (f));
42517721Speter}
42617721Speter
42717721Speter/*
42817721Speter * Unlink a file or dir, if possible.  If it is a directory do a deep
42917721Speter * removal of all of the files in the directory.  Return -1 on error
43017721Speter * (in which case errno is set).
43117721Speter */
43217721Speterint
43317721Speterunlink_file_dir (f)
43417721Speter    const char *f;
43517721Speter{
43634461Speter    struct stat sb;
43734461Speter
438175269Sobrien    /* This is called by the server parent process in contexts where
439175269Sobrien       it is not OK to send output (e.g. after we sent "ok" to the
440175269Sobrien       client).  */
441175269Sobrien    if (trace && !server_active)
44217721Speter	(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
44332785Speter
44417721Speter    if (noexec)
44517721Speter	return (0);
44617721Speter
44717721Speter    /* For at least some unices, if root tries to unlink() a directory,
44817721Speter       instead of doing something rational like returning EISDIR,
44917721Speter       the system will gleefully go ahead and corrupt the filesystem.
45034461Speter       So we first call stat() to see if it is OK to call unlink().  This
45117721Speter       doesn't quite work--if someone creates a directory between the
45234461Speter       call to stat() and the call to unlink(), we'll still corrupt
45317721Speter       the filesystem.  Where is the Unix Haters Handbook when you need
45417721Speter       it?  */
45534461Speter    if (stat (f, &sb) < 0)
45617721Speter    {
45734461Speter	if (existence_error (errno))
45834461Speter	{
45934461Speter	    /* The file or directory doesn't exist anyhow.  */
46017721Speter	    return -1;
46134461Speter	}
46217721Speter    }
46334461Speter    else if (S_ISDIR (sb.st_mode))
46434461Speter	return deep_remove_dir (f);
46534461Speter
46681407Speter    return CVS_UNLINK (f);
46717721Speter}
46817721Speter
46917721Speter/* Remove a directory and everything it contains.  Returns 0 for
47017721Speter * success, -1 for failure (in which case errno is set).
47117721Speter */
47217721Speter
47317721Speterstatic int
47417721Speterdeep_remove_dir (path)
47517721Speter    const char *path;
47617721Speter{
47717721Speter    DIR		  *dirp;
47817721Speter    struct dirent *dp;
47917721Speter
48025839Speter    if (rmdir (path) != 0)
48117721Speter    {
48225839Speter	if (errno == ENOTEMPTY
48325839Speter	    || errno == EEXIST
48425839Speter	    /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug
48525839Speter	       (it defines ENOTEMPTY and EEXIST to 17 but actually
48625839Speter	       returns 87).  */
48725839Speter	    || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87))
48817721Speter	{
48981407Speter	    if ((dirp = CVS_OPENDIR (path)) == NULL)
49025839Speter		/* If unable to open the directory return
49125839Speter		 * an error
49225839Speter		 */
49325839Speter		return -1;
49417721Speter
49554431Speter	    errno = 0;
49681407Speter	    while ((dp = CVS_READDIR (dirp)) != NULL)
49725839Speter	    {
49825839Speter		char *buf;
49917721Speter
50025839Speter		if (strcmp (dp->d_name, ".") == 0 ||
50125839Speter			    strcmp (dp->d_name, "..") == 0)
50225839Speter		    continue;
50325839Speter
50425839Speter		buf = xmalloc (strlen (path) + strlen (dp->d_name) + 5);
50525839Speter		sprintf (buf, "%s/%s", path, dp->d_name);
50625839Speter
50725839Speter		/* See comment in unlink_file_dir explanation of why we use
50825839Speter		   isdir instead of just calling unlink and checking the
50925839Speter		   status.  */
51025839Speter		if (isdir(buf))
51117721Speter		{
51225839Speter		    if (deep_remove_dir(buf))
51325839Speter		    {
51481407Speter			CVS_CLOSEDIR(dirp);
51525839Speter			free (buf);
51625839Speter			return -1;
51725839Speter		    }
51817721Speter		}
51925839Speter		else
52017721Speter		{
52181407Speter		    if (CVS_UNLINK (buf) != 0)
52225839Speter		    {
52381407Speter			CVS_CLOSEDIR(dirp);
52425839Speter			free (buf);
52525839Speter			return -1;
52625839Speter		    }
52717721Speter		}
52825839Speter		free (buf);
52954431Speter
53054431Speter		errno = 0;
53117721Speter	    }
53254431Speter	    if (errno != 0)
53354431Speter	    {
53454431Speter		int save_errno = errno;
53581407Speter		CVS_CLOSEDIR (dirp);
53654431Speter		errno = save_errno;
53754431Speter		return -1;
53854431Speter	    }
53981407Speter	    CVS_CLOSEDIR (dirp);
54025839Speter	    return rmdir (path);
54117721Speter	}
54225839Speter	else
54325839Speter	    return -1;
54425839Speter    }
54517721Speter
54617721Speter    /* Was able to remove the directory return 0 */
54717721Speter    return 0;
54817721Speter}
54917721Speter
55017721Speter/* Read NCHARS bytes from descriptor FD into BUF.
55117721Speter   Return the number of characters successfully read.
55217721Speter   The number returned is always NCHARS unless end-of-file or error.  */
55317721Speterstatic size_t
55417721Speterblock_read (fd, buf, nchars)
55517721Speter    int fd;
55617721Speter    char *buf;
55717721Speter    size_t nchars;
55817721Speter{
55917721Speter    char *bp = buf;
56017721Speter    size_t nread;
56117721Speter
56217721Speter    do
56317721Speter    {
56417721Speter	nread = read (fd, bp, nchars);
56517721Speter	if (nread == (size_t)-1)
56617721Speter	{
56717721Speter#ifdef EINTR
56817721Speter	    if (errno == EINTR)
56917721Speter		continue;
57017721Speter#endif
57117721Speter	    return (size_t)-1;
57217721Speter	}
57317721Speter
57417721Speter	if (nread == 0)
57517721Speter	    break;
57617721Speter
57717721Speter	bp += nread;
57817721Speter	nchars -= nread;
57917721Speter    } while (nchars != 0);
58017721Speter
58117721Speter    return bp - buf;
58217721Speter}
58317721Speter
58417721Speter
58517721Speter/*
58617721Speter * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
58734461Speter * If FILE1 and FILE2 are special files, compare their salient characteristics
58834461Speter * (i.e. major/minor device numbers, links, etc.
58917721Speter */
59017721Speterint
59117721Speterxcmp (file1, file2)
59217721Speter    const char *file1;
59317721Speter    const char *file2;
59417721Speter{
59517721Speter    char *buf1, *buf2;
59617721Speter    struct stat sb1, sb2;
59717721Speter    int fd1, fd2;
59817721Speter    int ret;
59917721Speter
60034461Speter    if (CVS_LSTAT (file1, &sb1) < 0)
60134461Speter	error (1, errno, "cannot lstat %s", file1);
60234461Speter    if (CVS_LSTAT (file2, &sb2) < 0)
60334461Speter	error (1, errno, "cannot lstat %s", file2);
60434461Speter
60534461Speter    /* If FILE1 and FILE2 are not the same file type, they are unequal. */
60634461Speter    if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT))
60734461Speter	return 1;
60834461Speter
60934461Speter    /* If FILE1 and FILE2 are symlinks, they are equal if they point to
61034461Speter       the same thing. */
611128269Speter#ifdef S_ISLNK
61234461Speter    if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode))
61334461Speter    {
61434461Speter	int result;
61534461Speter	buf1 = xreadlink (file1);
61634461Speter	buf2 = xreadlink (file2);
61734461Speter	result = (strcmp (buf1, buf2) == 0);
61834461Speter	free (buf1);
61934461Speter	free (buf2);
62034461Speter	return result;
62134461Speter    }
622128269Speter#endif
62334461Speter
62434461Speter    /* If FILE1 and FILE2 are devices, they are equal if their device
62534461Speter       numbers match. */
62634461Speter    if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode))
62734461Speter    {
628107487Speter#ifdef HAVE_STRUCT_STAT_ST_RDEV
62934461Speter	if (sb1.st_rdev == sb2.st_rdev)
63034461Speter	    return 0;
63134461Speter	else
63234461Speter	    return 1;
63366528Speter#else
63466528Speter	error (1, 0, "cannot compare device files on this system (%s and %s)",
63566528Speter	       file1, file2);
63666528Speter#endif
63734461Speter    }
63834461Speter
63917721Speter    if ((fd1 = open (file1, O_RDONLY)) < 0)
64017721Speter	error (1, errno, "cannot open file %s for comparing", file1);
64117721Speter    if ((fd2 = open (file2, O_RDONLY)) < 0)
64217721Speter	error (1, errno, "cannot open file %s for comparing", file2);
64317721Speter
64417721Speter    /* A generic file compare routine might compare st_dev & st_ino here
64517721Speter       to see if the two files being compared are actually the same file.
64617721Speter       But that won't happen in CVS, so we won't bother. */
64717721Speter
64817721Speter    if (sb1.st_size != sb2.st_size)
64917721Speter	ret = 1;
65017721Speter    else if (sb1.st_size == 0)
65117721Speter	ret = 0;
65217721Speter    else
65317721Speter    {
65417721Speter	/* FIXME: compute the optimal buffer size by computing the least
65517721Speter	   common multiple of the files st_blocks field */
65617721Speter	size_t buf_size = 8 * 1024;
65717721Speter	size_t read1;
65817721Speter	size_t read2;
65917721Speter
66017721Speter	buf1 = xmalloc (buf_size);
66117721Speter	buf2 = xmalloc (buf_size);
66217721Speter
66317721Speter	do
66417721Speter	{
66517721Speter	    read1 = block_read (fd1, buf1, buf_size);
66617721Speter	    if (read1 == (size_t)-1)
66717721Speter		error (1, errno, "cannot read file %s for comparing", file1);
66817721Speter
66917721Speter	    read2 = block_read (fd2, buf2, buf_size);
67017721Speter	    if (read2 == (size_t)-1)
67117721Speter		error (1, errno, "cannot read file %s for comparing", file2);
67217721Speter
67317721Speter	    /* assert (read1 == read2); */
67417721Speter
67517721Speter	    ret = memcmp(buf1, buf2, read1);
67617721Speter	} while (ret == 0 && read1 == buf_size);
67717721Speter
67817721Speter	free (buf1);
67917721Speter	free (buf2);
68017721Speter    }
68117721Speter
68217721Speter    (void) close (fd1);
68317721Speter    (void) close (fd2);
68417721Speter    return (ret);
68517721Speter}
68625839Speter
68725839Speter/* Generate a unique temporary filename.  Returns a pointer to a newly
68881407Speter * malloc'd string containing the name.  Returns successfully or not at
68981407Speter * all.
69081407Speter *
69181407Speter *     THIS FUNCTION IS DEPRECATED!!!  USE cvs_temp_file INSTEAD!!!
69281407Speter *
69381407Speter * and yes, I know about the way the rcs commands use temp files.  I think
69481407Speter * they should be converted too but I don't have time to look into it right
69581407Speter * now.
69681407Speter */
69754440Speterchar *
69854440Spetercvs_temp_name ()
69954440Speter{
70081407Speter    char *fn;
70181407Speter    FILE *fp;
70254440Speter
70381407Speter    fp = cvs_temp_file (&fn);
70481407Speter    if (fp == NULL)
705175269Sobrien	error (1, errno, "Failed to create temporary file %s",
706175269Sobrien	       fn ? fn : "(null)");
70781407Speter    if (fclose (fp) == EOF)
70881407Speter	error (0, errno, "Failed to close temporary file %s", fn);
70981407Speter    return fn;
71081407Speter}
71154440Speter
71281407Speter/* Generate a unique temporary filename and return an open file stream
71381407Speter * to the truncated file by that name
71481407Speter *
71581407Speter *  INPUTS
71681407Speter *	filename	where to place the pointer to the newly allocated file
71781407Speter *   			name string
71881407Speter *
71981407Speter *  OUTPUTS
72081407Speter *	filename	dereferenced, will point to the newly allocated file
72181407Speter *			name string.  This value is undefined if the function
72281407Speter *			returns an error.
72381407Speter *
72481407Speter *  RETURNS
72581407Speter *	An open file pointer to a read/write mode empty temporary file with the
72681407Speter *	unique file name or NULL on failure.
72781407Speter *
72881407Speter *  ERRORS
72981407Speter *	on error, errno will be set to some value either by CVS_FOPEN or
73081407Speter *	whatever system function is called to generate the temporary file name
73181407Speter */
73281407Speter/* There are at least four functions for generating temporary
73381407Speter * filenames.  We use mkstemp (BSD 4.3) if possible, else tempnam (SVID 3),
73481407Speter * else mktemp (BSD 4.3), and as last resort tmpnam (POSIX).  Reason is that
73581407Speter * mkstemp, tempnam, and mktemp both allow to specify the directory in which
73681407Speter * the temporary file will be created.
73781407Speter *
73881407Speter * And the _correct_ way to use the deprecated functions probably involves
73981407Speter * opening file descriptors using O_EXCL & O_CREAT and even doing the annoying
74081407Speter * NFS locking thing, but until I hear of more problems, I'm not going to
74181407Speter * bother.
74281407Speter */
743175269SobrienFILE *
744175269Sobriencvs_temp_file (filename)
74581407Speter    char **filename;
74625839Speter{
74781407Speter    char *fn;
74881407Speter    FILE *fp;
74917721Speter
75081407Speter    /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
75181407Speter     * some of the rcs & diff functions which rely on a temp file run in
75281407Speter     * noexec mode too.
75381407Speter     */
75481407Speter
75581407Speter    assert (filename != NULL);
75681407Speter
75781407Speter#ifdef HAVE_MKSTEMP
75881407Speter
75981407Speter    {
76081407Speter    int fd;
76181407Speter
76281407Speter    fn = xmalloc (strlen (Tmpdir) + 11);
76381407Speter    sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" );
76481407Speter    fd = mkstemp (fn);
76581407Speter
76681407Speter    /* a NULL return will be interpreted by callers as an error and
76781407Speter     * errno should still be set
76881407Speter     */
76981407Speter    if (fd == -1) fp = NULL;
77081407Speter    else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
77181407Speter    {
772128269Speter	/* Attempt to close and unlink the file since mkstemp returned
773128269Speter	 * sucessfully and we believe it's been created and opened.
77481407Speter	 */
77581407Speter 	int save_errno = errno;
77681407Speter	if (close (fd))
77781407Speter	    error (0, errno, "Failed to close temporary file %s", fn);
77881407Speter	if (CVS_UNLINK (fn))
77981407Speter	    error (0, errno, "Failed to unlink temporary file %s", fn);
78081407Speter	errno = save_errno;
78181407Speter    }
78281407Speter
783175269Sobrien    if (fp == NULL)
784175269Sobrien    {
785175269Sobrien	free (fn);
786175269Sobrien	fn = NULL;
787175269Sobrien    }
78881407Speter    /* mkstemp is defined to open mode 0600 using glibc 2.0.7+ */
78981407Speter    /* FIXME - configure can probably tell us which version of glibc we are
79081407Speter     * linking to and not chmod for 2.0.7+
79181407Speter     */
79281407Speter    else chmod (fn, 0600);
79381407Speter
79481407Speter    }
79581407Speter
79681407Speter#elif HAVE_TEMPNAM
79781407Speter
79881407Speter    /* tempnam has been deprecated due to under-specification */
79981407Speter
80081407Speter    fn = tempnam (Tmpdir, "cvs");
80181407Speter    if (fn == NULL) fp = NULL;
802175269Sobrien    else if ((fp = CVS_FOPEN (fn, "w+")) == NULL)
803175269Sobrien    {
804175269Sobrien	free (fn);
805175269Sobrien	fn = NULL;
806175269Sobrien    }
80781407Speter    else chmod (fn, 0600);
80881407Speter
80925839Speter    /* tempnam returns a pointer to a newly malloc'd string, so there's
81081407Speter     * no need for a xstrdup
81181407Speter     */
81217721Speter
81381407Speter#elif HAVE_MKTEMP
81425839Speter
81581407Speter    /* mktemp has been deprecated due to the BSD 4.3 specification specifying
81681407Speter     * that XXXXXX will be replaced by a PID and a letter, creating only 26
81781407Speter     * possibilities, a security risk, and a race condition.
81881407Speter     */
81925839Speter
82081407Speter    {
82181407Speter    char *ifn;
82281407Speter
82381407Speter    ifn = xmalloc (strlen (Tmpdir) + 11);
82481407Speter    sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" );
82581407Speter    fn = mktemp (ifn);
82681407Speter
82781407Speter    if (fn == NULL) fp = NULL;
82881407Speter    else fp = CVS_FOPEN (fn, "w+");
82981407Speter
83081407Speter    if (fp == NULL) free (ifn);
83181407Speter    else chmod (fn, 0600);
83281407Speter
83381407Speter    }
83481407Speter
83581407Speter#else	/* use tmpnam if all else fails */
83681407Speter
83781407Speter    /* tmpnam is deprecated */
83881407Speter
83981407Speter    {
84081407Speter    char ifn[L_tmpnam + 1];
84181407Speter
84281407Speter    fn = tmpnam (ifn);
84381407Speter
84481407Speter    if (fn == NULL) fp = NULL;
84581407Speter    else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL)
84681407Speter    {
84781407Speter	fn = xstrdup (ifn);
84881407Speter	chmod (fn, 0600);
84981407Speter    }
85081407Speter
85181407Speter    }
85281407Speter
85381407Speter#endif
85481407Speter
85581407Speter    *filename = fn;
856175269Sobrien    if (fn == NULL && fp != NULL)
857175269Sobrien    {
858175269Sobrien	fclose (fp);
859175269Sobrien	fp = NULL;
860175269Sobrien    }
86181407Speter    return fp;
86217721Speter}
86381407Speter
86417721Speter
865128269Speter
866128269Speter#ifdef HAVE_READLINK
867128269Speter/* char *
868128269Speter * xreadlink ( const char *link )
869128269Speter *
870128269Speter * Like the X/OPEN and 4.4BSD readlink() function, but allocates and returns
871128269Speter * its own buf.
872128269Speter *
873128269Speter * INPUTS
874128269Speter *  link	The original path.
875128269Speter *
876128269Speter * RETURNS
877128269Speter *  The resolution of the final symbolic link in the path.
878128269Speter *
879128269Speter * ERRORS
880128269Speter *  This function exits with a fatal error if it fails to read the link for
881128269Speter *  any reason.
88234461Speter */
883175269Sobrien#define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX)
884175269Sobrien
88534461Speterchar *
88634461Speterxreadlink (link)
88734461Speter    const char *link;
88834461Speter{
88934461Speter    char *file = NULL;
890175269Sobrien    size_t buflen = BUFSIZ;
89117721Speter
892175269Sobrien    /* Get the name of the file to which `from' is linked. */
893175269Sobrien    while (1)
89434461Speter    {
895175269Sobrien	ssize_t r;
896175269Sobrien	size_t link_name_len;
897175269Sobrien
89834461Speter	file = xrealloc (file, buflen);
899175269Sobrien	r = readlink (link, file, buflen);
900175269Sobrien	link_name_len = r;
90134461Speter
902175269Sobrien	if (r < 0
903175269Sobrien#ifdef ERANGE
904175269Sobrien	    /* AIX 4 and HP-UX report ERANGE if the buffer is too small. */
905175269Sobrien	    && errno != ERANGE
906175269Sobrien#endif
907175269Sobrien	    )
908175269Sobrien	    error (1, errno, "cannot readlink %s", link);
90934461Speter
910175269Sobrien	/* If there is space for the NUL byte, set it and return. */
911175269Sobrien	if (r >= 0 && link_name_len < buflen)
912175269Sobrien	{
913175269Sobrien	    file[link_name_len] = '\0';
914175269Sobrien	    return file;
915175269Sobrien	}
916175008Sobrien
917175269Sobrien	if (buflen <= MAXSIZE / 2)
918175269Sobrien	    buflen *= 2;
919175269Sobrien	else if (buflen < MAXSIZE)
920175269Sobrien	    buflen = MAXSIZE;
921175269Sobrien	else
922175269Sobrien	    /* Our buffer cannot grow any bigger.  */
923175269Sobrien	    error (1, ENAMETOOLONG, "cannot readlink %s", link);
924175269Sobrien    }
925128269Speter}
926128269Speter#endif /* HAVE_READLINK */
92744856Speter
928128269Speter
929128269Speter
930128269Speter/* char *
931128269Speter * xresolvepath ( const char *path )
932128269Speter *
933128269Speter * Like xreadlink(), but resolve all links in a path.
934128269Speter *
935128269Speter * INPUTS
936128269Speter *  path	The original path.
937128269Speter *
938128269Speter * RETURNS
939128269Speter *  The path with any symbolic links expanded.
940128269Speter *
941128269Speter * ERRORS
942128269Speter *  This function exits with a fatal error if it fails to read the link for
943128269Speter *  any reason.
944128269Speter */
945128269Speterchar *
946128269Speterxresolvepath ( path )
947128269Speter    const char *path;
948128269Speter{
949128269Speter    char *hardpath;
950128269Speter    char *owd;
951128269Speter
952128269Speter    assert ( isdir ( path ) );
953128269Speter
954128269Speter    /* FIXME - If HAVE_READLINK is defined, we should probably walk the path
955128269Speter     * bit by bit calling xreadlink().
956128269Speter     */
957128269Speter
958128269Speter    owd = xgetwd();
959128269Speter    if ( CVS_CHDIR ( path ) < 0)
960128269Speter	error ( 1, errno, "cannot chdir to %s", path );
961128269Speter    if ( ( hardpath = xgetwd() ) == NULL )
962128269Speter	error (1, errno, "cannot getwd in %s", path);
963128269Speter    if ( CVS_CHDIR ( owd ) < 0)
964128269Speter	error ( 1, errno, "cannot chdir to %s", owd );
965128269Speter    free (owd);
966128269Speter    return hardpath;
96734461Speter}
96834461Speter
96934461Speter
970128269Speter
97117721Speter/* Return a pointer into PATH's last component.  */
972128269Speterconst char *
97317721Speterlast_component (path)
974128269Speter    const char *path;
97517721Speter{
976128269Speter    const char *last = strrchr (path, '/');
977175269Sobrien
978175269Sobrien    assert (path);
97934461Speter    if (last && (last != path))
98017721Speter        return last + 1;
98117721Speter    else
98217721Speter        return path;
98317721Speter}
98417721Speter
98517721Speter/* Return the home directory.  Returns a pointer to storage
98625839Speter   managed by this function or its callees (currently getenv).
98725839Speter   This function will return the same thing every time it is
98832785Speter   called.  Returns NULL if there is no home directory.
98932785Speter
99032785Speter   Note that for a pserver server, this may return root's home
99132785Speter   directory.  What typically happens is that upon being started from
99232785Speter   inetd, before switching users, the code in cvsrc.c calls
99332785Speter   get_homedir which remembers root's home directory in the static
99432785Speter   variable.  Then the switch happens and get_homedir might return a
99532785Speter   directory that we don't even have read or execute permissions for
99632785Speter   (which is bad, when various parts of CVS try to read there).  One
99732785Speter   fix would be to make the value returned by get_homedir only good
99832785Speter   until the next call (which would free the old value).  Another fix
99932785Speter   would be to just always malloc our answer, and let the caller free
100032785Speter   it (that is best, because some day we may need to be reentrant).
100132785Speter
100232785Speter   The workaround is to put -f in inetd.conf which means that
100332785Speter   get_homedir won't get called until after the switch in user ID.
100432785Speter
100532785Speter   The whole concept of a "home directory" on the server is pretty
100632785Speter   iffy, although I suppose some people probably are relying on it for
100732785Speter   .cvsrc and such, in the cases where it works.  */
100817721Speterchar *
100917721Speterget_homedir ()
101017721Speter{
101125839Speter    static char *home = NULL;
101281407Speter    char *env;
101325839Speter    struct passwd *pw;
101425839Speter
101525839Speter    if (home != NULL)
101625839Speter	return home;
101725839Speter
1018175269Sobrien    if (!server_active && (env = getenv ("HOME")) != NULL)
101925839Speter	home = env;
102025839Speter    else if ((pw = (struct passwd *) getpwuid (getuid ()))
102125839Speter	     && pw->pw_dir)
102225839Speter	home = xstrdup (pw->pw_dir);
102325839Speter    else
102425839Speter	return 0;
102525839Speter
102625839Speter    return home;
102717721Speter}
102817721Speter
1029107487Speter/* Compose a path to a file in the home directory.  This is necessary because
1030107487Speter * of different behavior on UNIX and VMS.  See the notes in vms/filesubr.c.
1031107487Speter *
1032107487Speter * A more clean solution would be something more along the lines of a
1033107487Speter * "join a directory to a filename" kind of thing which was not specific to
1034107487Speter * the homedir.  This should aid portability between UNIX, Mac, Windows, VMS,
1035107487Speter * and possibly others.  This is already handled by Perl - it might be
1036107487Speter * interesting to see how much of the code was written in C since Perl is under
1037107487Speter * the GPL and the Artistic license - we might be able to use it.
1038107487Speter */
1039107487Speterchar *
1040107487Speterstrcat_filename_onto_homedir (dir, file)
1041107487Speter    const char *dir;
1042107487Speter    const char *file;
1043107487Speter{
1044107487Speter    char *path = xmalloc (strlen (dir) + 1 + strlen(file) + 1);
1045107487Speter    sprintf (path, "%s/%s", dir, file);
1046107487Speter    return path;
1047107487Speter}
1048107487Speter
104917721Speter/* See cvs.h for description.  On unix this does nothing, because the
105017721Speter   shell expands the wildcards.  */
105117721Spetervoid
105217721Speterexpand_wild (argc, argv, pargc, pargv)
105317721Speter    int argc;
105417721Speter    char **argv;
105517721Speter    int *pargc;
105617721Speter    char ***pargv;
105717721Speter{
105817721Speter    int i;
1059175269Sobrien    assert (argv || !argc);
1060130307Speter    if (size_overflow_p (xtimes (argc, sizeof (char *)))) {
1061130307Speter	*pargc = 0;
1062130307Speter	*pargv = NULL;
1063130307Speter	error (0, 0, "expand_wild: too many arguments");
1064130307Speter	return;
1065130307Speter    }
106617721Speter    *pargc = argc;
1067130307Speter    *pargv = xmalloc (xtimes (argc, sizeof (char *)));
106817721Speter    for (i = 0; i < argc; ++i)
106917721Speter	(*pargv)[i] = xstrdup (argv[i]);
107017721Speter}
107125839Speter
1072128269Speter
1073128269Speter
107425839Speter#ifdef SERVER_SUPPORT
107525839Speter/* Case-insensitive string compare.  I know that some systems
107625839Speter   have such a routine, but I'm not sure I see any reasons for
107725839Speter   dealing with the hair of figuring out whether they do (I haven't
107825839Speter   looked into whether this is a performance bottleneck; I would guess
107925839Speter   not).  */
108025839Speterint
108125839Spetercvs_casecmp (str1, str2)
1082128269Speter    const char *str1;
1083128269Speter    const char *str2;
108425839Speter{
1085128269Speter    const char *p;
1086128269Speter    const char *q;
108725839Speter    int pqdiff;
108825839Speter
108925839Speter    p = str1;
109025839Speter    q = str2;
109125839Speter    while ((pqdiff = tolower (*p) - tolower (*q)) == 0)
109225839Speter    {
109325839Speter	if (*p == '\0')
109425839Speter	    return 0;
109525839Speter	++p;
109625839Speter	++q;
109725839Speter    }
109825839Speter    return pqdiff;
109925839Speter}
110025839Speter#endif /* SERVER_SUPPORT */
1101