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