1 #include "arch.h"
2 #include "deco.h"
3 #include "extr.h"
4 #include "fs.h"
5 #include "link.h"
6 #include "mem.h"
7 #include "msg.h"
8 #include "str.h"
9 
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <stdbool.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <strings.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 
21 
complete_first_link(struct archive * a)22 static void complete_first_link(struct archive *a)
23 	{
24 	size_t ldot, ldir;
25 	char *old = a->links.file.e;
26 
27 	/* we are finally in possession of the sandbox name needed to build the link path */
28 	a->links.file.p = alloc(plen(a->sandbox) + 1 + elen(a->links.file) + 1);
29 	a->links.file.e = str_cp(str_cp(a->links.file.p, a->sandbox.p), "/");
30 	a->links.file.z = str_cp(a->links.file.e, old);
31 	free(old);
32 
33 	/* build the archive link target */
34 	ldot = Absolute || a->file.p[0] != '/' ? strlen("../") : 0;
35 	ldir = Absolute ? 0 : dlen(a->file);
36 	a->links.target.p = alloc(ldot + ldir + elen(a->file) + 1);
37 	a->links.target.e = str_cp_max(ldir, str_cp_max(ldot, a->links.target.p, "../"), a->file.p);
38 	a->links.target.z = str_cp(a->links.target.e, a->file.e);
39 	}
40 
is_aux(const struct archive * a,const char * e)41 static bool is_aux(const struct archive *a, const char *e)
42 	{
43 	return strncmp(a->links.file.e, e, a->lname) == 0                    /* same main name */
44 	       && strcasecmp(a->links.file.e + a->lname, e + a->lname) != 0  /* different exten */
45 	       && match_extr(a->extr, e) == a->lname;                        /* same match pos */
46 	}
47 
add_link(struct archive * a,const char * e)48 static void add_link(struct archive *a, const char *e)
49 	{
50 	struct link *l;
51 	size_t se = strlen(e) + 1;
52 
53 	l = alloc(sizeof(struct link));
54 	l->next = a->links.next;
55 	a->links.next = l;
56 
57 	l->file.p = alloc(dlen(a->links.file) + se);
58 	l->file.e = str_cp_max(dlen(a->links.file), l->file.p, a->links.file.p);
59 	l->file.z = str_cp_tr(tolower, str_cp_max(a->lname, l->file.e, e), e + a->lname);
60 	l->target.p = alloc(dlen(a->links.target) + se);
61 	l->target.e = str_cp_max(dlen(a->links.target), l->target.p, a->links.target.p);
62 	l->target.z = str_cp(l->target.e, e);
63 	}
64 
make_symlink(struct link * l)65 static bool make_symlink(struct link *l)
66 	{
67 	struct stat st;
68 
69 	if (symlink(l->target.p, l->file.p) != 0)
70 		return err(MSG_SYMLINK, errno, l->file.p);
71 	if (lstat(l->file.p, &st) != 0)
72 		return err(MSG_STAT, errno, l->file.p);
73 	l->inode = st.st_ino;
74 	l->ctime = st.st_ctime;
75 	return true;
76 	}
77 
create_links(struct archive * a)78 bool create_links(struct archive *a)
79 	{
80 	DIR *dir;
81 	struct link *l;
82 	struct dirent *ent;
83 	char bak;
84 
85 	complete_first_link(a);
86 
87 	if (dlen(a->file) == 0)
88 		dir = open_dir(".");
89 	else
90 		{
91 		bak = a->file.e[0];
92 		a->file.e[0] = '\0';
93 		dir = open_dir(a->file.p);
94 		a->file.e[0] = bak;
95 		}
96 	if (!dir)
97 		return false;
98 	while ((ent = readdir(dir)))
99 		if (is_aux(a, ent->d_name))
100 			add_link(a, ent->d_name);
101 	closedir(dir);
102 
103 	for (l = &a->links; l; l = l->next)
104 		if (!make_symlink(l))
105 			return false;
106 	return true;
107 	}
108 
del_link(const struct link * l)109 static bool del_link(const struct link *l)
110 	{
111 	struct stat st;
112 	ssize_t lread;
113 	char *target, *msg = NULL;
114 
115 	if (lstat(l->file.p, &st) != 0)
116 		return errno == ENOENT /* deleted */ || err(MSG_STAT, errno, l->file.p);
117 	if (l->inode != st.st_ino || l->ctime != st.st_ctime /* replaced */)
118 		return true;
119 
120 	target = alloc(plen(l->target) + 1);
121 	lread = readlink(l->file.p, target, plen(l->target) + 1);
122 	if (lread == -1)
123 		{
124 		if (errno != EINVAL)
125 			msg = MSG_READLINK;
126 		}
127 	else if ((size_t)lread == plen(l->target)
128 	         && memcmp(target, l->target.p, lread) == 0
129 	         && unlink(l->file.p) != 0
130 	         && errno != ENOENT)
131 		msg = MSG_DEL;
132 	free(target);
133 	return !msg || err(msg, errno, l->file.p);
134 	}
135 
del_links(const struct archive * a)136 bool del_links(const struct archive *a)
137 	{
138 	const struct link *l;
139 	bool success = true;
140 
141 	for (l = &a->links; l; l = l->next)
142 		if (!del_link(l))
143 			success = false;
144 	return success;
145 	}
146 
free_links(const struct archive * a)147 void free_links(const struct archive *a)
148 	{
149 	struct link *l, *next;
150 
151 	if (a->links.file.p /* we called complete_first_link() previously */)
152 		{
153 		for (l = (struct link *)&a->links; l; l = l->next)
154 			{
155 			free(l->file.p);
156 			free(l->target.p);
157 			}
158 		for (l = a->links.next; l; l = next)
159 			{
160 			next = l->next;
161 			free(l);
162 			}
163 		}
164 	else
165 		free(a->links.file.e);
166 	}
167