summaryrefslogtreecommitdiff
path: root/pith/osdep/canaccess.c
blob: 8dd97b262650134b12669245ea9e6e84b8238be9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: canaccess.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
#endif

/*
 * ========================================================================
 * Copyright 2006-2007 University of Washington
 * Copyright 2013-2014 Eduardo Chappa
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * ========================================================================
 */

#include <system.h>
#include "bldpath.h"
#include "fnexpand.h"
#include "../charconv/utf8.h"
#include "../charconv/filesys.h"
#include "canaccess.h"


/*
 * Useful definitions
 */
#ifdef	_WINDOWS

#define ACCESS_IN_CWD(F,M)	(can_access((F), (M)))
#define	PATH_SEP		';'
#define	FILE_SEP		'\\'

#else  /* UNIX */

#define ACCESS_IN_CWD(F,M)	(-1)
#define	PATH_SEP		':'
#define	FILE_SEP		'/'

#endif /* UNIX */



/*
 *     Check if we can access a file in a given way
 *
 * Args: file      -- The file to check
 *       mode      -- The mode ala the access() system call, see ACCESS_EXISTS
 *                    and friends in alpine.h.
 *
 * Result: returns 0 if the user can access the file according to the mode,
 *         -1 if he can't (and errno is set).
 *
 *
 */
int
can_access(char *file, int mode)
{
#ifdef	_WINDOWS
    struct stat buf;

    /*
     * NOTE: The WinNT access call returns that every directory is readable and
     * writable. We actually want to know if the write is going to fail, so we
     * try it. We don't read directories in Windows so we skip implementing that.
     */
    if(mode & WRITE_ACCESS && file && !our_stat(file, &buf) && (buf.st_mode & S_IFMT) == S_IFDIR){
	char *testname;
	int   fd;
	size_t l = 0;

	/*
	 * We'd like to just call temp_nam here, since it creates a file
	 * and does what we want. However, temp_nam calls us!
	 */
	if((testname = malloc(MAXPATH * sizeof(char)))){
	    strncpy(testname, file, MAXPATH-1);
	    testname[MAXPATH-1] = '\0';
	    if(testname[0] && testname[(l=strlen(testname))-1] != '\\' &&
	       l+1 < MAXPATH){
		l++;
		strncat(testname, "\\", MAXPATH-strlen(testname)-1);
		testname[MAXPATH-1] = '\0';
	    }
	    
	    if(l+8 < MAXPATH &&
	       strncat(testname, "caXXXXXX", MAXPATH-strlen(testname)-1) && mktemp(testname)){
		if((fd = our_open(testname, O_CREAT|O_EXCL|O_WRONLY|O_BINARY, 0600)) >= 0){
		    (void)close(fd);
		    our_unlink(testname);
		    free(testname);
		    /* success, drop through to access call */
		}
		else{
		    free(testname);
		    /* can't write in the directory */
		    return(-1);
		}
	    }
	    else{
		free(testname);
		return(-1);
	    }
	}
    }
    if(mode & EXECUTE_ACCESS)    /* Windows access has no execute mode */
      mode &= ~EXECUTE_ACCESS;   /* and crashes because of it */
#endif /* WINDOWS */

    return(our_access(file, mode));
}


/*----------------------------------------------------------------------
       Check if we can access a file in a given way in the given path

   Args: path     -- The path to look for "file" in
	 file      -- The file to check
         mode      -- The mode ala the access() system call, see ACCESS_EXISTS
                      and friends in alpine.h.

 Result: returns 0 if the user can access the file according to the mode,
         -1 if he can't (and errno is set).
 ----*/
int
can_access_in_path(char *path, char *file, int mode)
{
    char tmp[MAXPATH];
    int  rv = -1;

    if(!path || !*path || is_rooted_path(file)){
	rv = can_access(file, mode);
    }
    else if(is_homedir_path(file)){
	strncpy(tmp, file, sizeof(tmp));
	tmp[sizeof(tmp)-1] = '\0';
	rv = fnexpand(tmp, sizeof(tmp)) ? can_access(tmp, mode) : -1;
    }
    else if((rv = ACCESS_IN_CWD(file,mode)) < 0){
	char path_copy[MAXPATH + 1], *p, *t;

	if(strlen(path) < MAXPATH){
	    strncpy(path_copy, path, sizeof(path_copy));
	    path_copy[sizeof(path_copy)-1] = '\0';

	    for(p = path_copy; p && *p; p = t){
		if((t = strchr(p, PATH_SEP)) != NULL)
		  *t++ = '\0';

		snprintf(tmp, sizeof(tmp), "%s%c%s", p, FILE_SEP, file);
		if((rv = can_access(tmp, mode)) == 0)
		  break;
	    }
	}
    }

    return(rv);
}