1/*
2* I'm tired of doing "vsnprintf()" etc just to open a
3* file, so here's a "return static buffer with printf"
4* interface for paths.
5*
6* It's obviously not thread-safe. Sue me. But it's quite
7* useful for doing things like
8*
9* f = open(mkpath("%s/%s.git", base, name), O_RDONLY);
10*
11* which is what it's designed for.
12*/
13#include "cache.h"
1415
static char bad_path[] = "/bad-path/";
1617
static char *get_pathname(void)
18{
19static char pathname_array[4][PATH_MAX];
20static int index;
21return pathname_array[3 & ++index];
22}
2324
static char *cleanup_path(char *path)
25{
26/* Clean it up */
27if (!memcmp(path, "./", 2)) {
28path += 2;
29while (*path == '/')
30path++;
31}
32return path;
33}
3435
char *mkpath(const char *fmt, ...)
36{
37va_list args;
38unsigned len;
39char *pathname = get_pathname();
4041
va_start(args, fmt);
42len = vsnprintf(pathname, PATH_MAX, fmt, args);
43va_end(args);
44if (len >= PATH_MAX)
45return bad_path;
46return cleanup_path(pathname);
47}
4849
char *git_path(const char *fmt, ...)
50{
51const char *git_dir = get_git_dir();
52char *pathname = get_pathname();
53va_list args;
54unsigned len;
5556
len = strlen(git_dir);
57if (len > PATH_MAX-100)
58return bad_path;
59memcpy(pathname, git_dir, len);
60if (len && git_dir[len-1] != '/')
61pathname[len++] = '/';
62va_start(args, fmt);
63len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
64va_end(args);
65if (len >= PATH_MAX)
66return bad_path;
67return cleanup_path(pathname);
68}
6970
71
/* git_mkstemp() - create tmp file honoring TMPDIR variable */
72int git_mkstemp(char *path, size_t len, const char *template)
73{
74const char *tmp;
75size_t n;
7677
tmp = getenv("TMPDIR");
78if (!tmp)
79tmp = "/tmp";
80n = snprintf(path, len, "%s/%s", tmp, template);
81if (len <= n) {
82errno = ENAMETOOLONG;
83return -1;
84}
85return mkstemp(path);
86}
8788
89
int validate_headref(const char *path)
90{
91struct stat st;
92char *buf, buffer[256];
93unsigned char sha1[20];
94int fd;
95ssize_t len;
9697
if (lstat(path, &st) < 0)
98return -1;
99100
/* Make sure it is a "refs/.." symlink */
101if (S_ISLNK(st.st_mode)) {
102len = readlink(path, buffer, sizeof(buffer)-1);
103if (len >= 5 && !memcmp("refs/", buffer, 5))
104return 0;
105return -1;
106}
107108
/*
109* Anything else, just open it and try to see if it is a symbolic ref.
110*/
111fd = open(path, O_RDONLY);
112if (fd < 0)
113return -1;
114len = read_in_full(fd, buffer, sizeof(buffer)-1);
115close(fd);
116117
/*
118* Is it a symbolic ref?
119*/
120if (len < 4)
121return -1;
122if (!memcmp("ref:", buffer, 4)) {
123buf = buffer + 4;
124len -= 4;
125while (len && isspace(*buf))
126buf++, len--;
127if (len >= 5 && !memcmp("refs/", buf, 5))
128return 0;
129}
130131
/*
132* Is this a detached HEAD?
133*/
134if (!get_sha1_hex(buffer, sha1))
135return 0;
136137
return -1;
138}
139140
static char *user_path(char *buf, char *path, int sz)
141{
142struct passwd *pw;
143char *slash;
144int len, baselen;
145146
if (!path || path[0] != '~')
147return NULL;
148path++;
149slash = strchr(path, '/');
150if (path[0] == '/' || !path[0]) {
151pw = getpwuid(getuid());
152}
153else {
154if (slash) {
155*slash = 0;
156pw = getpwnam(path);
157*slash = '/';
158}
159else
160pw = getpwnam(path);
161}
162if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
163return NULL;
164baselen = strlen(pw->pw_dir);
165memcpy(buf, pw->pw_dir, baselen);
166while ((1 < baselen) && (buf[baselen-1] == '/')) {
167buf[baselen-1] = 0;
168baselen--;
169}
170if (slash && slash[1]) {
171len = strlen(slash);
172if (sz <= baselen + len)
173return NULL;
174memcpy(buf + baselen, slash, len + 1);
175}
176return buf;
177}
178179
/*
180* First, one directory to try is determined by the following algorithm.
181*
182* (0) If "strict" is given, the path is used as given and no DWIM is
183* done. Otherwise:
184* (1) "~/path" to mean path under the running user's home directory;
185* (2) "~user/path" to mean path under named user's home directory;
186* (3) "relative/path" to mean cwd relative directory; or
187* (4) "/absolute/path" to mean absolute directory.
188*
189* Unless "strict" is given, we try access() for existence of "%s.git/.git",
190* "%s/.git", "%s.git", "%s" in this order. The first one that exists is
191* what we try.
192*
193* Second, we try chdir() to that. Upon failure, we return NULL.
194*
195* Then, we try if the current directory is a valid git repository.
196* Upon failure, we return NULL.
197*
198* If all goes well, we return the directory we used to chdir() (but
199* before ~user is expanded), avoiding getcwd() resolving symbolic
200* links. User relative paths are also returned as they are given,
201* except DWIM suffixing.
202*/
203char *enter_repo(char *path, int strict)
204{
205static char used_path[PATH_MAX];
206static char validated_path[PATH_MAX];
207208
if (!path)
209return NULL;
210211
if (!strict) {
212static const char *suffix[] = {
213".git/.git", "/.git", ".git", "", NULL,
214};
215int len = strlen(path);
216int i;
217while ((1 < len) && (path[len-1] == '/')) {
218path[len-1] = 0;
219len--;
220}
221if (PATH_MAX <= len)
222return NULL;
223if (path[0] == '~') {
224if (!user_path(used_path, path, PATH_MAX))
225return NULL;
226strcpy(validated_path, path);
227path = used_path;
228}
229else if (PATH_MAX - 10 < len)
230return NULL;
231else {
232path = strcpy(used_path, path);
233strcpy(validated_path, path);
234}
235len = strlen(path);
236for (i = 0; suffix[i]; i++) {
237strcpy(path + len, suffix[i]);
238if (!access(path, F_OK)) {
239strcat(validated_path, suffix[i]);
240break;
241}
242}
243if (!suffix[i] || chdir(path))
244return NULL;
245path = validated_path;
246}
247else if (chdir(path))
248return NULL;
249250
if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
251validate_headref("HEAD") == 0) {
252setenv(GIT_DIR_ENVIRONMENT, ".", 1);
253check_repository_format();
254return path;
255}
256257
return NULL;
258}
259260
int adjust_shared_perm(const char *path)
261{
262struct stat st;
263int mode;
264265
if (!shared_repository)
266return 0;
267if (lstat(path, &st) < 0)
268return -1;
269mode = st.st_mode;
270271
if (shared_repository) {
272int tweak = shared_repository;
273if (!(mode & S_IWUSR))
274tweak &= ~0222;
275mode = (mode & ~0777) | tweak;
276} else {
277/* Preserve old PERM_UMASK behaviour */
278if (mode & S_IWUSR)
279mode |= S_IWGRP;
280}
281282
if (S_ISDIR(mode)) {
283mode |= FORCE_DIR_SET_GID;
284285
/* Copy read bits to execute bits */
286mode |= (shared_repository & 0444) >> 2;
287}
288289
if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
290return -2;
291return 0;
292}
293294
/* We allow "recursive" symbolic links. Only within reason, though. */
295#define MAXDEPTH 5
296297
const char *make_absolute_path(const char *path)
298{
299static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
300char cwd[1024] = "";
301int buf_index = 1, len;
302303
int depth = MAXDEPTH;
304char *last_elem = NULL;
305struct stat st;
306307
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
308die ("Too long path: %.*s", 60, path);
309310
while (depth--) {
311if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
312char *last_slash = strrchr(buf, '/');
313if (last_slash) {
314*last_slash = '\0';
315last_elem = xstrdup(last_slash + 1);
316} else {
317last_elem = xstrdup(buf);
318*buf = '\0';
319}
320}
321322
if (*buf) {
323if (!*cwd && !getcwd(cwd, sizeof(cwd)))
324die ("Could not get current working directory");
325326
if (chdir(buf))
327die ("Could not switch to '%s'", buf);
328}
329if (!getcwd(buf, PATH_MAX))
330die ("Could not get current working directory");
331332
if (last_elem) {
333int len = strlen(buf);
334if (len + strlen(last_elem) + 2 > PATH_MAX)
335die ("Too long path name: '%s/%s'",
336buf, last_elem);
337buf[len] = '/';
338strcpy(buf + len + 1, last_elem);
339free(last_elem);
340last_elem = NULL;
341}
342343
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
344len = readlink(buf, next_buf, PATH_MAX);
345if (len < 0)
346die ("Invalid symlink: %s", buf);
347next_buf[len] = '\0';
348buf = next_buf;
349buf_index = 1 - buf_index;
350next_buf = bufs[buf_index];
351} else
352break;
353}
354355
if (*cwd && chdir(cwd))
356die ("Could not change back to '%s'", cwd);
357358
return buf;
359}
360361
/*
362* path = absolute path
363* buf = buffer of at least max(2, strlen(path)+1) bytes
364* It is okay if buf == path, but they should not overlap otherwise.
365*
366* Performs the following normalizations on path, storing the result in buf:
367* - Removes trailing slashes.
368* - Removes empty components.
369* - Removes "." components.
370* - Removes ".." components, and the components the precede them.
371* "" and paths that contain only slashes are normalized to "/".
372* Returns the length of the output.
373*
374* Note that this function is purely textual. It does not follow symlinks,
375* verify the existence of the path, or make any system calls.
376*/
377int normalize_absolute_path(char *buf, const char *path)
378{
379const char *comp_start = path, *comp_end = path;
380char *dst = buf;
381int comp_len;
382assert(buf);
383assert(path);
384385
while (*comp_start) {
386assert(*comp_start == '/');
387while (*++comp_end && *comp_end != '/')
388; /* nothing */
389comp_len = comp_end - comp_start;
390391
if (!strncmp("/", comp_start, comp_len) ||
392!strncmp("/.", comp_start, comp_len))
393goto next;
394395
if (!strncmp("/..", comp_start, comp_len)) {
396while (dst > buf && *--dst != '/')
397; /* nothing */
398goto next;
399}
400401
memcpy(dst, comp_start, comp_len);
402dst += comp_len;
403next:
404comp_start = comp_end;
405}
406407
if (dst == buf)
408*dst++ = '/';
409410
*dst = '\0';
411return dst - buf;
412}