1 |
|
|
/* $OpenBSD: file.c,v 1.273 2017/06/01 08:38:56 joris Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2006 Joris Vink <joris@openbsd.org> |
4 |
|
|
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. The name of the author may not be used to endorse or promote products |
14 |
|
|
* derived from this software without specific prior written permission. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
17 |
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
18 |
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
19 |
|
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
20 |
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
21 |
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
22 |
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
23 |
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
24 |
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
25 |
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
#include <sys/types.h> |
29 |
|
|
#include <sys/mman.h> |
30 |
|
|
#include <sys/stat.h> |
31 |
|
|
#include <sys/time.h> |
32 |
|
|
|
33 |
|
|
#include <dirent.h> |
34 |
|
|
#include <errno.h> |
35 |
|
|
#include <fcntl.h> |
36 |
|
|
#include <fnmatch.h> |
37 |
|
|
#include <libgen.h> |
38 |
|
|
#include <stdint.h> |
39 |
|
|
#include <stdlib.h> |
40 |
|
|
#include <string.h> |
41 |
|
|
#include <unistd.h> |
42 |
|
|
|
43 |
|
|
#include "atomicio.h" |
44 |
|
|
#include "cvs.h" |
45 |
|
|
#include "remote.h" |
46 |
|
|
|
47 |
|
|
#define CVS_IGN_STATIC 0x01 /* pattern is static, no need to glob */ |
48 |
|
|
|
49 |
|
|
#define CVS_CHAR_ISMETA(c) ((c == '*') || (c == '?') || (c == '[')) |
50 |
|
|
|
51 |
|
|
extern int print_stdout; |
52 |
|
|
extern int build_dirs; |
53 |
|
|
|
54 |
|
|
/* |
55 |
|
|
* Standard patterns to ignore. |
56 |
|
|
*/ |
57 |
|
|
static const char *cvs_ign_std[] = { |
58 |
|
|
".", |
59 |
|
|
"..", |
60 |
|
|
"*.o", |
61 |
|
|
"*.a", |
62 |
|
|
"*.bak", |
63 |
|
|
"*.orig", |
64 |
|
|
"*.rej", |
65 |
|
|
"*.old", |
66 |
|
|
"*.exe", |
67 |
|
|
"*.depend", |
68 |
|
|
"*.obj", |
69 |
|
|
"*.elc", |
70 |
|
|
"*.ln", |
71 |
|
|
"*.olb", |
72 |
|
|
"CVS", |
73 |
|
|
"core", |
74 |
|
|
"cvslog*", |
75 |
|
|
"*.core", |
76 |
|
|
".#*", |
77 |
|
|
"*~", |
78 |
|
|
"_$*", |
79 |
|
|
"*$", |
80 |
|
|
}; |
81 |
|
|
|
82 |
|
|
char *cvs_directory_tag = NULL; |
83 |
|
|
struct ignore_head cvs_ign_pats; |
84 |
|
|
struct ignore_head dir_ign_pats; |
85 |
|
|
struct ignore_head checkout_ign_pats; |
86 |
|
|
|
87 |
|
|
RB_GENERATE(cvs_flisthead, cvs_filelist, flist, cvs_filelist_cmp); |
88 |
|
|
|
89 |
|
|
void |
90 |
|
|
cvs_file_init(void) |
91 |
|
|
{ |
92 |
|
|
int i; |
93 |
|
|
FILE *ifp; |
94 |
|
|
char path[PATH_MAX], buf[MAXNAMLEN]; |
95 |
|
|
|
96 |
|
|
TAILQ_INIT(&cvs_ign_pats); |
97 |
|
|
TAILQ_INIT(&dir_ign_pats); |
98 |
|
|
TAILQ_INIT(&checkout_ign_pats); |
99 |
|
|
|
100 |
|
|
/* standard patterns to ignore */ |
101 |
|
|
for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++) |
102 |
|
|
cvs_file_ignore(cvs_ign_std[i], &cvs_ign_pats); |
103 |
|
|
|
104 |
|
|
if (cvs_homedir == NULL) |
105 |
|
|
return; |
106 |
|
|
|
107 |
|
|
/* read the cvsignore file in the user's home directory, if any */ |
108 |
|
|
(void)xsnprintf(path, PATH_MAX, "%s/.cvsignore", cvs_homedir); |
109 |
|
|
|
110 |
|
|
ifp = fopen(path, "r"); |
111 |
|
|
if (ifp == NULL) { |
112 |
|
|
if (errno != ENOENT) |
113 |
|
|
cvs_log(LP_ERRNO, |
114 |
|
|
"failed to open user's cvsignore file `%s'", path); |
115 |
|
|
} else { |
116 |
|
|
while (fgets(buf, MAXNAMLEN, ifp) != NULL) { |
117 |
|
|
buf[strcspn(buf, "\n")] = '\0'; |
118 |
|
|
if (buf[0] == '\0') |
119 |
|
|
continue; |
120 |
|
|
|
121 |
|
|
cvs_file_ignore(buf, &cvs_ign_pats); |
122 |
|
|
} |
123 |
|
|
|
124 |
|
|
(void)fclose(ifp); |
125 |
|
|
} |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
void |
129 |
|
|
cvs_file_ignore(const char *pat, struct ignore_head *list) |
130 |
|
|
{ |
131 |
|
|
char *cp; |
132 |
|
|
size_t len; |
133 |
|
|
struct cvs_ignpat *ip; |
134 |
|
|
|
135 |
|
|
ip = xmalloc(sizeof(*ip)); |
136 |
|
|
len = strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat)); |
137 |
|
|
if (len >= sizeof(ip->ip_pat)) |
138 |
|
|
fatal("cvs_file_ignore: truncation of pattern '%s'", pat); |
139 |
|
|
|
140 |
|
|
/* check if we will need globbing for that pattern */ |
141 |
|
|
ip->ip_flags = CVS_IGN_STATIC; |
142 |
|
|
for (cp = ip->ip_pat; *cp != '\0'; cp++) { |
143 |
|
|
if (CVS_CHAR_ISMETA(*cp)) { |
144 |
|
|
ip->ip_flags &= ~CVS_IGN_STATIC; |
145 |
|
|
break; |
146 |
|
|
} |
147 |
|
|
} |
148 |
|
|
|
149 |
|
|
TAILQ_INSERT_TAIL(list, ip, ip_list); |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
int |
153 |
|
|
cvs_file_chkign(const char *file) |
154 |
|
|
{ |
155 |
|
|
int flags; |
156 |
|
|
struct cvs_ignpat *ip; |
157 |
|
|
|
158 |
|
|
flags = FNM_PERIOD; |
159 |
|
|
if (cvs_nocase) |
160 |
|
|
flags |= FNM_CASEFOLD; |
161 |
|
|
|
162 |
|
|
TAILQ_FOREACH(ip, &cvs_ign_pats, ip_list) { |
163 |
|
|
if (ip->ip_flags & CVS_IGN_STATIC) { |
164 |
|
|
if (cvs_file_cmpname(file, ip->ip_pat) == 0) |
165 |
|
|
return (1); |
166 |
|
|
} else if (fnmatch(ip->ip_pat, file, flags) == 0) |
167 |
|
|
return (1); |
168 |
|
|
} |
169 |
|
|
|
170 |
|
|
TAILQ_FOREACH(ip, &dir_ign_pats, ip_list) { |
171 |
|
|
if (ip->ip_flags & CVS_IGN_STATIC) { |
172 |
|
|
if (cvs_file_cmpname(file, ip->ip_pat) == 0) |
173 |
|
|
return (1); |
174 |
|
|
} else if (fnmatch(ip->ip_pat, file, flags) == 0) |
175 |
|
|
return (1); |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
TAILQ_FOREACH(ip, &checkout_ign_pats, ip_list) { |
179 |
|
|
if (ip->ip_flags & CVS_IGN_STATIC) { |
180 |
|
|
if (cvs_file_cmpname(file, ip->ip_pat) == 0) |
181 |
|
|
return (1); |
182 |
|
|
} else if (fnmatch(ip->ip_pat, file, flags) == 0) |
183 |
|
|
return (1); |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
return (0); |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
void |
190 |
|
|
cvs_file_run(int argc, char **argv, struct cvs_recursion *cr) |
191 |
|
|
{ |
192 |
|
|
int i; |
193 |
|
|
struct cvs_flisthead fl; |
194 |
|
|
|
195 |
|
|
RB_INIT(&fl); |
196 |
|
|
|
197 |
|
|
for (i = 0; i < argc; i++) { |
198 |
|
|
STRIP_SLASH(argv[i]); |
199 |
|
|
cvs_file_get(argv[i], FILE_USER_SUPPLIED, &fl, 0); |
200 |
|
|
} |
201 |
|
|
|
202 |
|
|
cvs_file_walklist(&fl, cr); |
203 |
|
|
cvs_file_freelist(&fl); |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
struct cvs_filelist * |
207 |
|
|
cvs_file_get(char *name, int flags, struct cvs_flisthead *fl, int type) |
208 |
|
|
{ |
209 |
|
|
char *p; |
210 |
|
|
struct cvs_filelist *l, find; |
211 |
|
|
|
212 |
|
|
for (p = name; p[0] == '.' && p[1] == '/';) |
213 |
|
|
p += 2; |
214 |
|
|
|
215 |
|
|
find.file_path = p; |
216 |
|
|
l = RB_FIND(cvs_flisthead, fl, &find); |
217 |
|
|
if (l != NULL) |
218 |
|
|
return (l); |
219 |
|
|
|
220 |
|
|
l = xmalloc(sizeof(*l)); |
221 |
|
|
l->file_path = xstrdup(p); |
222 |
|
|
l->flags = flags; |
223 |
|
|
l->type = type; |
224 |
|
|
|
225 |
|
|
RB_INSERT(cvs_flisthead, fl, l); |
226 |
|
|
return (l); |
227 |
|
|
} |
228 |
|
|
|
229 |
|
|
struct cvs_file * |
230 |
|
|
cvs_file_get_cf(const char *d, const char *f, const char *fpath, int fd, |
231 |
|
|
int type, int flags) |
232 |
|
|
{ |
233 |
|
|
const char *p; |
234 |
|
|
struct cvs_file *cf; |
235 |
|
|
|
236 |
|
|
for (p = fpath; p[0] == '.' && p[1] == '/';) |
237 |
|
|
p += 2; |
238 |
|
|
|
239 |
|
|
cf = xcalloc(1, sizeof(*cf)); |
240 |
|
|
|
241 |
|
|
cf->file_name = xstrdup(f); |
242 |
|
|
cf->file_wd = xstrdup(d); |
243 |
|
|
cf->file_path = xstrdup(p); |
244 |
|
|
cf->fd = fd; |
245 |
|
|
cf->repo_fd = -1; |
246 |
|
|
cf->file_type = type; |
247 |
|
|
cf->file_status = 0; |
248 |
|
|
cf->file_flags = flags; |
249 |
|
|
cf->in_attic = 0; |
250 |
|
|
cf->file_ent = NULL; |
251 |
|
|
|
252 |
|
|
if (cf->fd != -1) |
253 |
|
|
cf->file_flags |= FILE_ON_DISK; |
254 |
|
|
|
255 |
|
|
if (cvsroot_is_remote() || cvs_server_active == 1) |
256 |
|
|
cvs_validate_directory(cf->file_path); |
257 |
|
|
|
258 |
|
|
return (cf); |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
void |
262 |
|
|
cvs_file_walklist(struct cvs_flisthead *fl, struct cvs_recursion *cr) |
263 |
|
|
{ |
264 |
|
|
int fd, type; |
265 |
|
|
struct stat st; |
266 |
|
|
struct cvs_file *cf; |
267 |
|
|
struct cvs_filelist *l, *nxt; |
268 |
|
|
char *d, *f, repo[PATH_MAX], fpath[PATH_MAX]; |
269 |
|
|
|
270 |
|
|
for (l = RB_MIN(cvs_flisthead, fl); l != NULL; l = nxt) { |
271 |
|
|
if (cvs_quit) |
272 |
|
|
fatal("received signal %d", sig_received); |
273 |
|
|
|
274 |
|
|
cvs_log(LP_TRACE, "cvs_file_walklist: element '%s'", |
275 |
|
|
l->file_path); |
276 |
|
|
|
277 |
|
|
if ((f = basename(l->file_path)) == NULL) |
278 |
|
|
fatal("cvs_file_walklist: basename failed"); |
279 |
|
|
if ((d = dirname(l->file_path)) == NULL) |
280 |
|
|
fatal("cvs_file_walklist: dirname failed"); |
281 |
|
|
|
282 |
|
|
type = l->type; |
283 |
|
|
if ((fd = open(l->file_path, O_RDONLY)) != -1) { |
284 |
|
|
if (type == 0) { |
285 |
|
|
if (fstat(fd, &st) == -1) { |
286 |
|
|
cvs_log(LP_ERRNO, "%s", l->file_path); |
287 |
|
|
(void)close(fd); |
288 |
|
|
goto next; |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
if (S_ISDIR(st.st_mode)) |
292 |
|
|
type = CVS_DIR; |
293 |
|
|
else if (S_ISREG(st.st_mode)) |
294 |
|
|
type = CVS_FILE; |
295 |
|
|
else { |
296 |
|
|
cvs_log(LP_ERR, |
297 |
|
|
"ignoring bad file type for %s", |
298 |
|
|
l->file_path); |
299 |
|
|
(void)close(fd); |
300 |
|
|
goto next; |
301 |
|
|
} |
302 |
|
|
} |
303 |
|
|
} else if (cvsroot_is_local()) { |
304 |
|
|
/* |
305 |
|
|
* During checkout -p, do not use any locally |
306 |
|
|
* available directories. |
307 |
|
|
*/ |
308 |
|
|
if ((cmdp->cmd_flags & CVS_USE_WDIR) && |
309 |
|
|
(cvs_cmdop != CVS_OP_CHECKOUT || !print_stdout)) |
310 |
|
|
if (stat(d, &st) == -1) { |
311 |
|
|
cvs_log(LP_ERRNO, "%s", d); |
312 |
|
|
goto next; |
313 |
|
|
} |
314 |
|
|
|
315 |
|
|
cvs_get_repository_path(d, repo, PATH_MAX); |
316 |
|
|
(void)xsnprintf(fpath, PATH_MAX, "%s/%s", |
317 |
|
|
repo, f); |
318 |
|
|
|
319 |
|
|
if ((fd = open(fpath, O_RDONLY)) == -1) { |
320 |
|
|
strlcat(fpath, RCS_FILE_EXT, PATH_MAX); |
321 |
|
|
fd = open(fpath, O_RDONLY); |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
if (fd != -1 && type == 0) { |
325 |
|
|
if (fstat(fd, &st) == -1) |
326 |
|
|
fatal("cvs_file_walklist: %s: %s", |
327 |
|
|
fpath, strerror(errno)); |
328 |
|
|
|
329 |
|
|
if (S_ISDIR(st.st_mode)) |
330 |
|
|
type = CVS_DIR; |
331 |
|
|
else if (S_ISREG(st.st_mode)) |
332 |
|
|
type = CVS_FILE; |
333 |
|
|
else { |
334 |
|
|
cvs_log(LP_ERR, |
335 |
|
|
"ignoring bad file type for %s", |
336 |
|
|
l->file_path); |
337 |
|
|
(void)close(fd); |
338 |
|
|
goto next; |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
/* this file is not in our working copy yet */ |
342 |
|
|
(void)close(fd); |
343 |
|
|
fd = -1; |
344 |
|
|
} else if (fd != -1) { |
345 |
|
|
close(fd); |
346 |
|
|
fd = -1; |
347 |
|
|
} |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
cf = cvs_file_get_cf(d, f, l->file_path, fd, type, l->flags); |
351 |
|
|
if (cf->file_type == CVS_DIR) { |
352 |
|
|
cvs_file_walkdir(cf, cr); |
353 |
|
|
} else { |
354 |
|
|
if (l->flags & FILE_USER_SUPPLIED) { |
355 |
|
|
cvs_parse_tagfile(cf->file_wd, |
356 |
|
|
&cvs_directory_tag, NULL, NULL); |
357 |
|
|
|
358 |
|
|
if (cvs_directory_tag == NULL && |
359 |
|
|
cvs_specified_tag != NULL) |
360 |
|
|
cvs_directory_tag = |
361 |
|
|
xstrdup(cvs_specified_tag); |
362 |
|
|
|
363 |
|
|
if (cvsroot_is_local()) { |
364 |
|
|
cvs_get_repository_path(cf->file_wd, |
365 |
|
|
repo, PATH_MAX); |
366 |
|
|
cvs_repository_lock(repo, |
367 |
|
|
(cmdp->cmd_flags & CVS_LOCK_REPO)); |
368 |
|
|
} |
369 |
|
|
} |
370 |
|
|
|
371 |
|
|
if (cr->fileproc != NULL) |
372 |
|
|
cr->fileproc(cf); |
373 |
|
|
|
374 |
|
|
if (l->flags & FILE_USER_SUPPLIED) { |
375 |
|
|
if (cvsroot_is_local() && |
376 |
|
|
(cmdp->cmd_flags & CVS_LOCK_REPO)) { |
377 |
|
|
cvs_repository_unlock(repo); |
378 |
|
|
} |
379 |
|
|
free(cvs_directory_tag); |
380 |
|
|
cvs_directory_tag = NULL; |
381 |
|
|
} |
382 |
|
|
} |
383 |
|
|
|
384 |
|
|
cvs_file_free(cf); |
385 |
|
|
|
386 |
|
|
next: |
387 |
|
|
nxt = RB_NEXT(cvs_flisthead, fl, l); |
388 |
|
|
} |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
void |
392 |
|
|
cvs_file_walkdir(struct cvs_file *cf, struct cvs_recursion *cr) |
393 |
|
|
{ |
394 |
|
|
int l, type; |
395 |
|
|
FILE *fp; |
396 |
|
|
int nbytes; |
397 |
|
|
size_t bufsize; |
398 |
|
|
struct stat st; |
399 |
|
|
struct dirent *dp; |
400 |
|
|
struct cvs_ent *ent; |
401 |
|
|
struct cvs_ignpat *ip; |
402 |
|
|
struct cvs_ent_line *line; |
403 |
|
|
struct cvs_flisthead fl, dl; |
404 |
|
|
CVSENTRIES *entlist; |
405 |
|
|
char *buf, *ebuf, *cp, repo[PATH_MAX], fpath[PATH_MAX]; |
406 |
|
|
|
407 |
|
|
cvs_log(LP_TRACE, "cvs_file_walkdir(%s)", cf->file_path); |
408 |
|
|
|
409 |
|
|
if (cr->enterdir != NULL) |
410 |
|
|
cr->enterdir(cf); |
411 |
|
|
|
412 |
|
|
if (cr->fileproc != NULL) |
413 |
|
|
cr->fileproc(cf); |
414 |
|
|
|
415 |
|
|
if (cf->file_status == FILE_SKIP) |
416 |
|
|
return; |
417 |
|
|
|
418 |
|
|
/* |
419 |
|
|
* If this is a repository-only command, do not touch any |
420 |
|
|
* locally available directories or try to create them. |
421 |
|
|
*/ |
422 |
|
|
if (!(cmdp->cmd_flags & CVS_USE_WDIR)) { |
423 |
|
|
RB_INIT(&fl); |
424 |
|
|
RB_INIT(&dl); |
425 |
|
|
goto walkrepo; |
426 |
|
|
} |
427 |
|
|
|
428 |
|
|
/* |
429 |
|
|
* If we do not have an admin directory inside here, dont bother, |
430 |
|
|
* unless we are running export or import. |
431 |
|
|
*/ |
432 |
|
|
(void)xsnprintf(fpath, PATH_MAX, "%s/%s", cf->file_path, |
433 |
|
|
CVS_PATH_CVSDIR); |
434 |
|
|
|
435 |
|
|
l = stat(fpath, &st); |
436 |
|
|
if (cvs_cmdop != CVS_OP_EXPORT && cvs_cmdop != CVS_OP_IMPORT && |
437 |
|
|
(l == -1 || (l == 0 && !S_ISDIR(st.st_mode)))) { |
438 |
|
|
return; |
439 |
|
|
} |
440 |
|
|
|
441 |
|
|
cvs_parse_tagfile(cf->file_path, &cvs_directory_tag, NULL, NULL); |
442 |
|
|
|
443 |
|
|
/* |
444 |
|
|
* check for a local .cvsignore file |
445 |
|
|
*/ |
446 |
|
|
(void)xsnprintf(fpath, PATH_MAX, "%s/.cvsignore", cf->file_path); |
447 |
|
|
|
448 |
|
|
if ((fp = fopen(fpath, "r")) != NULL) { |
449 |
|
|
while (fgets(fpath, PATH_MAX, fp) != NULL) { |
450 |
|
|
fpath[strcspn(fpath, "\n")] = '\0'; |
451 |
|
|
if (fpath[0] == '\0') |
452 |
|
|
continue; |
453 |
|
|
|
454 |
|
|
cvs_file_ignore(fpath, &dir_ign_pats); |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
(void)fclose(fp); |
458 |
|
|
} |
459 |
|
|
|
460 |
|
|
if (fstat(cf->fd, &st) == -1) |
461 |
|
|
fatal("cvs_file_walkdir: %s %s", cf->file_path, |
462 |
|
|
strerror(errno)); |
463 |
|
|
|
464 |
|
|
if ((uintmax_t)st.st_size > SIZE_MAX) |
465 |
|
|
fatal("cvs_file_walkdir: %s: file size too big", cf->file_name); |
466 |
|
|
|
467 |
|
|
bufsize = (st.st_size > st.st_blksize) ? st.st_size : st.st_blksize; |
468 |
|
|
|
469 |
|
|
buf = xmalloc(bufsize); |
470 |
|
|
RB_INIT(&fl); |
471 |
|
|
RB_INIT(&dl); |
472 |
|
|
|
473 |
|
|
while ((nbytes = getdents(cf->fd, buf, bufsize)) > 0) { |
474 |
|
|
ebuf = buf + nbytes; |
475 |
|
|
cp = buf; |
476 |
|
|
|
477 |
|
|
while (cp < ebuf) { |
478 |
|
|
dp = (struct dirent *)cp; |
479 |
|
|
if (!strcmp(dp->d_name, ".") || |
480 |
|
|
!strcmp(dp->d_name, "..") || |
481 |
|
|
!strcmp(dp->d_name, CVS_PATH_CVSDIR) || |
482 |
|
|
dp->d_fileno == 0) { |
483 |
|
|
cp += dp->d_reclen; |
484 |
|
|
continue; |
485 |
|
|
} |
486 |
|
|
|
487 |
|
|
(void)xsnprintf(fpath, PATH_MAX, "%s/%s", |
488 |
|
|
cf->file_path, dp->d_name); |
489 |
|
|
|
490 |
|
|
if (cvs_file_chkign(dp->d_name) && |
491 |
|
|
cvs_cmdop != CVS_OP_RLOG && |
492 |
|
|
cvs_cmdop != CVS_OP_RTAG) { |
493 |
|
|
if (cvs_cmdop == CVS_OP_IMPORT) |
494 |
|
|
cvs_import_ignored(fpath); |
495 |
|
|
cp += dp->d_reclen; |
496 |
|
|
continue; |
497 |
|
|
} |
498 |
|
|
|
499 |
|
|
/* |
500 |
|
|
* nfs and afs will show d_type as DT_UNKNOWN |
501 |
|
|
* for files and/or directories so when we encounter |
502 |
|
|
* this we call lstat() on the path to be sure. |
503 |
|
|
*/ |
504 |
|
|
if (dp->d_type == DT_UNKNOWN) { |
505 |
|
|
if (lstat(fpath, &st) == -1) |
506 |
|
|
fatal("'%s': %s", fpath, |
507 |
|
|
strerror(errno)); |
508 |
|
|
|
509 |
|
|
switch (st.st_mode & S_IFMT) { |
510 |
|
|
case S_IFDIR: |
511 |
|
|
type = CVS_DIR; |
512 |
|
|
break; |
513 |
|
|
case S_IFREG: |
514 |
|
|
type = CVS_FILE; |
515 |
|
|
break; |
516 |
|
|
default: |
517 |
|
|
type = FILE_SKIP; |
518 |
|
|
break; |
519 |
|
|
} |
520 |
|
|
} else { |
521 |
|
|
switch (dp->d_type) { |
522 |
|
|
case DT_DIR: |
523 |
|
|
type = CVS_DIR; |
524 |
|
|
break; |
525 |
|
|
case DT_REG: |
526 |
|
|
type = CVS_FILE; |
527 |
|
|
break; |
528 |
|
|
default: |
529 |
|
|
type = FILE_SKIP; |
530 |
|
|
break; |
531 |
|
|
} |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
if (type == FILE_SKIP) { |
535 |
|
|
if (verbosity > 1) { |
536 |
|
|
cvs_log(LP_NOTICE, "ignoring `%s'", |
537 |
|
|
dp->d_name); |
538 |
|
|
} |
539 |
|
|
cp += dp->d_reclen; |
540 |
|
|
continue; |
541 |
|
|
} |
542 |
|
|
|
543 |
|
|
switch (type) { |
544 |
|
|
case CVS_DIR: |
545 |
|
|
if (cr->flags & CR_RECURSE_DIRS) |
546 |
|
|
cvs_file_get(fpath, 0, &dl, CVS_DIR); |
547 |
|
|
break; |
548 |
|
|
case CVS_FILE: |
549 |
|
|
cvs_file_get(fpath, 0, &fl, CVS_FILE); |
550 |
|
|
break; |
551 |
|
|
default: |
552 |
|
|
fatal("type %d unknown, shouldn't happen", |
553 |
|
|
type); |
554 |
|
|
} |
555 |
|
|
|
556 |
|
|
cp += dp->d_reclen; |
557 |
|
|
} |
558 |
|
|
} |
559 |
|
|
|
560 |
|
|
if (nbytes == -1) |
561 |
|
|
fatal("cvs_file_walkdir: %s %s", cf->file_path, |
562 |
|
|
strerror(errno)); |
563 |
|
|
|
564 |
|
|
free(buf); |
565 |
|
|
|
566 |
|
|
while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) { |
567 |
|
|
TAILQ_REMOVE(&dir_ign_pats, ip, ip_list); |
568 |
|
|
free(ip); |
569 |
|
|
} |
570 |
|
|
|
571 |
|
|
entlist = cvs_ent_open(cf->file_path); |
572 |
|
|
TAILQ_FOREACH(line, &(entlist->cef_ent), entries_list) { |
573 |
|
|
ent = cvs_ent_parse(line->buf); |
574 |
|
|
|
575 |
|
|
(void)xsnprintf(fpath, PATH_MAX, "%s/%s", cf->file_path, |
576 |
|
|
ent->ce_name); |
577 |
|
|
|
578 |
|
|
if (!(cr->flags & CR_RECURSE_DIRS) && |
579 |
|
|
ent->ce_type == CVS_ENT_DIR) |
580 |
|
|
continue; |
581 |
|
|
if (ent->ce_type == CVS_ENT_DIR) |
582 |
|
|
cvs_file_get(fpath, 0, &dl, CVS_DIR); |
583 |
|
|
else if (ent->ce_type == CVS_ENT_FILE) |
584 |
|
|
cvs_file_get(fpath, 0, &fl, CVS_FILE); |
585 |
|
|
|
586 |
|
|
cvs_ent_free(ent); |
587 |
|
|
} |
588 |
|
|
|
589 |
|
|
walkrepo: |
590 |
|
|
if (cvsroot_is_local()) { |
591 |
|
|
cvs_get_repository_path(cf->file_path, repo, PATH_MAX); |
592 |
|
|
cvs_repository_lock(repo, (cmdp->cmd_flags & CVS_LOCK_REPO)); |
593 |
|
|
} |
594 |
|
|
|
595 |
|
|
if (cr->flags & CR_REPO) { |
596 |
|
|
xsnprintf(fpath, sizeof(fpath), "%s/%s", cf->file_path, |
597 |
|
|
CVS_PATH_STATICENTRIES); |
598 |
|
|
|
599 |
|
|
if (!(cmdp->cmd_flags & CVS_USE_WDIR) || |
600 |
|
|
stat(fpath, &st) == -1 || build_dirs == 1) |
601 |
|
|
cvs_repository_getdir(repo, cf->file_path, &fl, &dl, |
602 |
|
|
(cr->flags & CR_RECURSE_DIRS) ? |
603 |
|
|
REPOSITORY_DODIRS : 0); |
604 |
|
|
} |
605 |
|
|
|
606 |
|
|
cvs_file_walklist(&fl, cr); |
607 |
|
|
cvs_file_freelist(&fl); |
608 |
|
|
|
609 |
|
|
if (cvsroot_is_local() && (cmdp->cmd_flags & CVS_LOCK_REPO)) |
610 |
|
|
cvs_repository_unlock(repo); |
611 |
|
|
|
612 |
|
|
if (cvs_directory_tag != NULL && cmdp->cmd_flags & CVS_USE_WDIR) { |
613 |
|
|
cvs_write_tagfile(cf->file_path, cvs_directory_tag, NULL); |
614 |
|
|
free(cvs_directory_tag); |
615 |
|
|
cvs_directory_tag = NULL; |
616 |
|
|
} |
617 |
|
|
|
618 |
|
|
cvs_file_walklist(&dl, cr); |
619 |
|
|
cvs_file_freelist(&dl); |
620 |
|
|
|
621 |
|
|
if (cr->leavedir != NULL) |
622 |
|
|
cr->leavedir(cf); |
623 |
|
|
} |
624 |
|
|
|
625 |
|
|
void |
626 |
|
|
cvs_file_freelist(struct cvs_flisthead *fl) |
627 |
|
|
{ |
628 |
|
|
struct cvs_filelist *f, *nxt; |
629 |
|
|
|
630 |
|
|
for (f = RB_MIN(cvs_flisthead, fl); f != NULL; f = nxt) { |
631 |
|
|
nxt = RB_NEXT(cvs_flisthead, fl, f); |
632 |
|
|
RB_REMOVE(cvs_flisthead, fl, f); |
633 |
|
|
free(f->file_path); |
634 |
|
|
free(f); |
635 |
|
|
} |
636 |
|
|
} |
637 |
|
|
|
638 |
|
|
void |
639 |
|
|
cvs_file_classify(struct cvs_file *cf, const char *tag) |
640 |
|
|
{ |
641 |
|
|
size_t len; |
642 |
|
|
struct stat st; |
643 |
|
|
BUF *b1, *b2; |
644 |
|
|
int server_has_file, notag; |
645 |
|
|
int rflags, ismodified, rcsdead; |
646 |
|
|
CVSENTRIES *entlist = NULL; |
647 |
|
|
const char *state; |
648 |
|
|
char repo[PATH_MAX], rcsfile[PATH_MAX]; |
649 |
|
|
|
650 |
|
|
cvs_log(LP_TRACE, "cvs_file_classify(%s, %s)", cf->file_path, |
651 |
|
|
(tag != NULL) ? tag : "none"); |
652 |
|
|
|
653 |
|
|
if (!strcmp(cf->file_path, ".")) { |
654 |
|
|
cf->file_status = FILE_UPTODATE; |
655 |
|
|
return; |
656 |
|
|
} |
657 |
|
|
|
658 |
|
|
cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); |
659 |
|
|
(void)xsnprintf(rcsfile, PATH_MAX, "%s/%s", |
660 |
|
|
repo, cf->file_name); |
661 |
|
|
|
662 |
|
|
if (cf->file_type == CVS_FILE) { |
663 |
|
|
len = strlcat(rcsfile, RCS_FILE_EXT, PATH_MAX); |
664 |
|
|
if (len >= PATH_MAX) |
665 |
|
|
fatal("cvs_file_classify: truncation"); |
666 |
|
|
} |
667 |
|
|
|
668 |
|
|
cf->file_rpath = xstrdup(rcsfile); |
669 |
|
|
|
670 |
|
|
if (cmdp->cmd_flags & CVS_USE_WDIR) { |
671 |
|
|
entlist = cvs_ent_open(cf->file_wd); |
672 |
|
|
cf->file_ent = cvs_ent_get(entlist, cf->file_name); |
673 |
|
|
} else |
674 |
|
|
cf->file_ent = NULL; |
675 |
|
|
|
676 |
|
|
if (cf->file_ent != NULL) { |
677 |
|
|
if (cvs_specified_tag == NULL) |
678 |
|
|
tag = cf->file_ent->ce_tag; |
679 |
|
|
|
680 |
|
|
if (cf->file_flags & FILE_ON_DISK && |
681 |
|
|
cf->file_ent->ce_type == CVS_ENT_FILE && |
682 |
|
|
cf->file_type == CVS_DIR && tag != NULL) { |
683 |
|
|
cf->file_status = FILE_SKIP; |
684 |
|
|
return; |
685 |
|
|
} |
686 |
|
|
|
687 |
|
|
if (cf->file_flags & FILE_ON_DISK && |
688 |
|
|
cf->file_ent->ce_type == CVS_ENT_DIR && |
689 |
|
|
cf->file_type == CVS_FILE && tag != NULL) { |
690 |
|
|
cf->file_status = FILE_SKIP; |
691 |
|
|
return; |
692 |
|
|
} |
693 |
|
|
|
694 |
|
|
if (cf->file_flags & FILE_INSIDE_ATTIC && |
695 |
|
|
cf->file_ent->ce_type == CVS_ENT_DIR && |
696 |
|
|
cf->file_type != CVS_DIR) { |
697 |
|
|
cf->file_status = FILE_SKIP; |
698 |
|
|
return; |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
if (cf->file_flags & FILE_ON_DISK && |
702 |
|
|
cf->file_ent->ce_type == CVS_ENT_DIR && |
703 |
|
|
cf->file_type != CVS_DIR) |
704 |
|
|
fatal("%s is supposed to be a directory, but it is not", |
705 |
|
|
cf->file_path); |
706 |
|
|
if (cf->file_flags & FILE_ON_DISK && |
707 |
|
|
cf->file_ent->ce_type == CVS_ENT_FILE && |
708 |
|
|
cf->file_type != CVS_FILE) |
709 |
|
|
fatal("%s is supposed to be a file, but it is not", |
710 |
|
|
cf->file_path); |
711 |
|
|
} |
712 |
|
|
|
713 |
|
|
if (cf->file_type == CVS_DIR) { |
714 |
|
|
if (!(cmdp->cmd_flags & CVS_USE_WDIR)) |
715 |
|
|
cf->file_status = FILE_UPTODATE; |
716 |
|
|
else if (cf->fd == -1 && stat(rcsfile, &st) != -1) |
717 |
|
|
cf->file_status = DIR_CREATE; |
718 |
|
|
else if (cf->file_ent != NULL || cvs_cmdop == CVS_OP_RLOG || |
719 |
|
|
cvs_cmdop == CVS_OP_RTAG) |
720 |
|
|
cf->file_status = FILE_UPTODATE; |
721 |
|
|
else |
722 |
|
|
cf->file_status = FILE_UNKNOWN; |
723 |
|
|
|
724 |
|
|
return; |
725 |
|
|
} |
726 |
|
|
|
727 |
|
|
rflags = RCS_READ; |
728 |
|
|
switch (cvs_cmdop) { |
729 |
|
|
case CVS_OP_COMMIT: |
730 |
|
|
case CVS_OP_TAG: |
731 |
|
|
case CVS_OP_RTAG: |
732 |
|
|
rflags = RCS_WRITE; |
733 |
|
|
break; |
734 |
|
|
case CVS_OP_ADMIN: |
735 |
|
|
case CVS_OP_IMPORT: |
736 |
|
|
case CVS_OP_LOG: |
737 |
|
|
case CVS_OP_RLOG: |
738 |
|
|
rflags |= RCS_PARSE_FULLY; |
739 |
|
|
break; |
740 |
|
|
default: |
741 |
|
|
break; |
742 |
|
|
} |
743 |
|
|
|
744 |
|
|
cf->repo_fd = open(cf->file_rpath, O_RDONLY); |
745 |
|
|
if (cf->repo_fd != -1) { |
746 |
|
|
cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags); |
747 |
|
|
if (cf->file_rcs == NULL) |
748 |
|
|
fatal("cvs_file_classify: failed to parse RCS"); |
749 |
|
|
} else { |
750 |
|
|
(void)xsnprintf(rcsfile, PATH_MAX, "%s/%s/%s%s", |
751 |
|
|
repo, CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); |
752 |
|
|
|
753 |
|
|
cf->repo_fd = open(rcsfile, O_RDONLY); |
754 |
|
|
if (cf->repo_fd != -1) { |
755 |
|
|
free(cf->file_rpath); |
756 |
|
|
cf->file_rpath = xstrdup(rcsfile); |
757 |
|
|
cf->file_rcs = rcs_open(cf->file_rpath, |
758 |
|
|
cf->repo_fd, rflags); |
759 |
|
|
if (cf->file_rcs == NULL) |
760 |
|
|
fatal("cvs_file_classify: failed to parse RCS"); |
761 |
|
|
cf->in_attic = 1; |
762 |
|
|
} else { |
763 |
|
|
cf->file_rcs = NULL; |
764 |
|
|
} |
765 |
|
|
} |
766 |
|
|
|
767 |
|
|
notag = 0; |
768 |
|
|
cf->file_flags |= FILE_HAS_TAG; |
769 |
|
|
if (tag != NULL && cf->file_rcs != NULL) { |
770 |
|
|
if ((cf->file_rcsrev = rcs_translate_tag(tag, cf->file_rcs)) |
771 |
|
|
== NULL) { |
772 |
|
|
cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); |
773 |
|
|
if (cf->file_rcsrev != NULL) { |
774 |
|
|
notag = 1; |
775 |
|
|
cf->file_flags &= ~FILE_HAS_TAG; |
776 |
|
|
} |
777 |
|
|
} |
778 |
|
|
} else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) { |
779 |
|
|
cf->file_rcsrev = rcsnum_alloc(); |
780 |
|
|
rcsnum_cpy(cf->file_ent->ce_rev, cf->file_rcsrev, 0); |
781 |
|
|
} else if (cf->file_rcs != NULL) { |
782 |
|
|
cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); |
783 |
|
|
} else { |
784 |
|
|
cf->file_rcsrev = NULL; |
785 |
|
|
} |
786 |
|
|
|
787 |
|
|
ismodified = rcsdead = 0; |
788 |
|
|
if ((cf->file_flags & FILE_ON_DISK) && cf->file_ent != NULL) { |
789 |
|
|
if (fstat(cf->fd, &st) == -1) |
790 |
|
|
fatal("cvs_file_classify: %s", strerror(errno)); |
791 |
|
|
|
792 |
|
|
if (st.st_mtime != cf->file_ent->ce_mtime) |
793 |
|
|
ismodified = 1; |
794 |
|
|
} |
795 |
|
|
|
796 |
|
|
server_has_file = 0; |
797 |
|
|
if (cvs_server_active == 1 && cf->file_ent != NULL && |
798 |
|
|
cf->file_ent->ce_mtime == CVS_SERVER_UPTODATE) { |
799 |
|
|
server_has_file = 1; |
800 |
|
|
ismodified = 0; |
801 |
|
|
} |
802 |
|
|
|
803 |
|
|
if ((server_has_file == 1) || (cf->fd != -1)) |
804 |
|
|
cf->file_flags |= FILE_ON_DISK; |
805 |
|
|
|
806 |
|
|
if (ismodified == 1 && |
807 |
|
|
(cf->file_flags & FILE_ON_DISK) && cf->file_rcs != NULL && |
808 |
|
|
cf->file_ent != NULL && !RCSNUM_ISBRANCH(cf->file_ent->ce_rev) && |
809 |
|
|
cf->file_ent->ce_status != CVS_ENT_ADDED) { |
810 |
|
|
b1 = rcs_rev_getbuf(cf->file_rcs, cf->file_ent->ce_rev, 0); |
811 |
|
|
b2 = buf_load_fd(cf->fd); |
812 |
|
|
|
813 |
|
|
if (buf_differ(b1, b2)) |
814 |
|
|
ismodified = 1; |
815 |
|
|
else |
816 |
|
|
ismodified = 0; |
817 |
|
|
buf_free(b1); |
818 |
|
|
buf_free(b2); |
819 |
|
|
} |
820 |
|
|
|
821 |
|
|
if (cf->file_rcs != NULL && cf->file_rcsrev != NULL && |
822 |
|
|
!RCSNUM_ISBRANCH(cf->file_rcsrev)) { |
823 |
|
|
state = rcs_state_get(cf->file_rcs, cf->file_rcsrev); |
824 |
|
|
if (state == NULL) |
825 |
|
|
fatal("failed to get state for HEAD for %s", |
826 |
|
|
cf->file_path); |
827 |
|
|
if (!strcmp(state, RCS_STATE_DEAD)) |
828 |
|
|
rcsdead = 1; |
829 |
|
|
|
830 |
|
|
if (cvs_specified_date == -1 && cvs_directory_date == -1 && |
831 |
|
|
tag == NULL && cf->in_attic && |
832 |
|
|
!RCSNUM_ISBRANCHREV(cf->file_rcsrev)) |
833 |
|
|
rcsdead = 1; |
834 |
|
|
|
835 |
|
|
cf->file_rcs->rf_dead = rcsdead; |
836 |
|
|
} |
837 |
|
|
|
838 |
|
|
/* |
839 |
|
|
* 10 Sin |
840 |
|
|
* 20 Goto hell |
841 |
|
|
* (I welcome you if-else hell) |
842 |
|
|
*/ |
843 |
|
|
if (cf->file_ent == NULL) { |
844 |
|
|
if (cf->file_rcs == NULL) { |
845 |
|
|
if (!(cf->file_flags & FILE_ON_DISK)) { |
846 |
|
|
cvs_log(LP_NOTICE, |
847 |
|
|
"nothing known about '%s'", |
848 |
|
|
cf->file_path); |
849 |
|
|
} |
850 |
|
|
|
851 |
|
|
cf->file_status = FILE_UNKNOWN; |
852 |
|
|
} else if (rcsdead == 1 || !(cf->file_flags & FILE_HAS_TAG)) { |
853 |
|
|
if (!(cf->file_flags & FILE_ON_DISK)) { |
854 |
|
|
cf->file_status = FILE_UPTODATE; |
855 |
|
|
} else if (cvs_cmdop != CVS_OP_ADD) { |
856 |
|
|
cf->file_status = FILE_UNKNOWN; |
857 |
|
|
} |
858 |
|
|
} else if (notag == 0 && cf->file_rcsrev != NULL) { |
859 |
|
|
cf->file_status = FILE_CHECKOUT; |
860 |
|
|
} else { |
861 |
|
|
cf->file_status = FILE_UPTODATE; |
862 |
|
|
} |
863 |
|
|
|
864 |
|
|
return; |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
switch (cf->file_ent->ce_status) { |
868 |
|
|
case CVS_ENT_ADDED: |
869 |
|
|
if (!(cf->file_flags & FILE_ON_DISK)) { |
870 |
|
|
if (cvs_cmdop != CVS_OP_REMOVE) { |
871 |
|
|
cvs_log(LP_NOTICE, |
872 |
|
|
"warning: new-born %s has disappeared", |
873 |
|
|
cf->file_path); |
874 |
|
|
} |
875 |
|
|
cf->file_status = FILE_REMOVE_ENTRY; |
876 |
|
|
} else if (cf->file_rcs == NULL || rcsdead == 1 || |
877 |
|
|
!(cf->file_flags & FILE_HAS_TAG)) { |
878 |
|
|
cf->file_status = FILE_ADDED; |
879 |
|
|
} else { |
880 |
|
|
cvs_log(LP_NOTICE, |
881 |
|
|
"conflict: %s already created by others", |
882 |
|
|
cf->file_path); |
883 |
|
|
cf->file_status = FILE_CONFLICT; |
884 |
|
|
} |
885 |
|
|
break; |
886 |
|
|
case CVS_ENT_REMOVED: |
887 |
|
|
if (cf->file_flags & FILE_ON_DISK) { |
888 |
|
|
cvs_log(LP_NOTICE, |
889 |
|
|
"%s should be removed but is still there", |
890 |
|
|
cf->file_path); |
891 |
|
|
cf->file_status = FILE_REMOVED; |
892 |
|
|
} else if (cf->file_rcs == NULL || rcsdead == 1) { |
893 |
|
|
cf->file_status = FILE_REMOVE_ENTRY; |
894 |
|
|
} else { |
895 |
|
|
if (rcsnum_differ(cf->file_ent->ce_rev, |
896 |
|
|
cf->file_rcsrev) && cvs_cmdop != CVS_OP_ADD) { |
897 |
|
|
cvs_log(LP_NOTICE, |
898 |
|
|
"conflict: removed %s was modified" |
899 |
|
|
" by a second party", |
900 |
|
|
cf->file_path); |
901 |
|
|
cf->file_status = FILE_CONFLICT; |
902 |
|
|
} else { |
903 |
|
|
cf->file_status = FILE_REMOVED; |
904 |
|
|
} |
905 |
|
|
} |
906 |
|
|
break; |
907 |
|
|
case CVS_ENT_REG: |
908 |
|
|
if (cf->file_rcs == NULL || cf->file_rcsrev == NULL || |
909 |
|
|
rcsdead == 1 || (reset_tag == 1 && cf->in_attic == 1) || |
910 |
|
|
(notag == 1 && tag != NULL)) { |
911 |
|
|
if (!(cf->file_flags & FILE_ON_DISK)) { |
912 |
|
|
cvs_log(LP_NOTICE, |
913 |
|
|
"warning: %s's entry exists but" |
914 |
|
|
" is no longer in the repository," |
915 |
|
|
" removing entry", |
916 |
|
|
cf->file_path); |
917 |
|
|
cf->file_status = FILE_REMOVE_ENTRY; |
918 |
|
|
} else { |
919 |
|
|
if (ismodified) { |
920 |
|
|
cvs_log(LP_NOTICE, |
921 |
|
|
"conflict: %s is no longer " |
922 |
|
|
"in the repository but is " |
923 |
|
|
"locally modified", |
924 |
|
|
cf->file_path); |
925 |
|
|
if (cvs_cmdop == CVS_OP_COMMIT) |
926 |
|
|
cf->file_status = FILE_UNLINK; |
927 |
|
|
else |
928 |
|
|
cf->file_status = FILE_CONFLICT; |
929 |
|
|
} else if (cvs_cmdop != CVS_OP_IMPORT) { |
930 |
|
|
cvs_log(LP_NOTICE, |
931 |
|
|
"%s is no longer in the " |
932 |
|
|
"repository", |
933 |
|
|
cf->file_path); |
934 |
|
|
|
935 |
|
|
cf->file_status = FILE_UNLINK; |
936 |
|
|
} |
937 |
|
|
} |
938 |
|
|
} else if (cf->file_rcsrev == NULL) { |
939 |
|
|
cf->file_status = FILE_UNLINK; |
940 |
|
|
} else { |
941 |
|
|
if (!(cf->file_flags & FILE_ON_DISK)) { |
942 |
|
|
if (cvs_cmdop != CVS_OP_REMOVE) { |
943 |
|
|
cvs_log(LP_NOTICE, |
944 |
|
|
"warning: %s was lost", |
945 |
|
|
cf->file_path); |
946 |
|
|
} |
947 |
|
|
cf->file_status = FILE_LOST; |
948 |
|
|
} else { |
949 |
|
|
if (ismodified == 1) |
950 |
|
|
cf->file_status = FILE_MODIFIED; |
951 |
|
|
else |
952 |
|
|
cf->file_status = FILE_UPTODATE; |
953 |
|
|
if (rcsnum_differ(cf->file_ent->ce_rev, |
954 |
|
|
cf->file_rcsrev)) { |
955 |
|
|
if (cf->file_status == FILE_MODIFIED) |
956 |
|
|
cf->file_status = FILE_MERGE; |
957 |
|
|
else |
958 |
|
|
cf->file_status = FILE_PATCH; |
959 |
|
|
} |
960 |
|
|
} |
961 |
|
|
} |
962 |
|
|
break; |
963 |
|
|
case CVS_ENT_UNKNOWN: |
964 |
|
|
if (cvs_server_active != 1) |
965 |
|
|
fatal("server-side questionable in local mode?"); |
966 |
|
|
cf->file_status = FILE_UNKNOWN; |
967 |
|
|
break; |
968 |
|
|
default: |
969 |
|
|
break; |
970 |
|
|
} |
971 |
|
|
} |
972 |
|
|
|
973 |
|
|
void |
974 |
|
|
cvs_file_free(struct cvs_file *cf) |
975 |
|
|
{ |
976 |
|
|
free(cf->file_name); |
977 |
|
|
free(cf->file_wd); |
978 |
|
|
free(cf->file_path); |
979 |
|
|
free(cf->file_rcsrev); |
980 |
|
|
free(cf->file_rpath); |
981 |
|
|
if (cf->file_ent != NULL) |
982 |
|
|
cvs_ent_free(cf->file_ent); |
983 |
|
|
if (cf->file_rcs != NULL) |
984 |
|
|
rcs_close(cf->file_rcs); |
985 |
|
|
if (cf->fd != -1) |
986 |
|
|
(void)close(cf->fd); |
987 |
|
|
if (cf->repo_fd != -1) |
988 |
|
|
(void)close(cf->repo_fd); |
989 |
|
|
free(cf); |
990 |
|
|
} |
991 |
|
|
|
992 |
|
|
int |
993 |
|
|
cvs_file_cmpname(const char *name1, const char *name2) |
994 |
|
|
{ |
995 |
|
|
return (cvs_nocase == 0) ? (strcmp(name1, name2)) : |
996 |
|
|
(strcasecmp(name1, name2)); |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
int |
1000 |
|
|
cvs_file_cmp(const char *file1, const char *file2) |
1001 |
|
|
{ |
1002 |
|
|
struct stat stb1, stb2; |
1003 |
|
|
int fd1, fd2, ret; |
1004 |
|
|
|
1005 |
|
|
ret = 0; |
1006 |
|
|
|
1007 |
|
|
if ((fd1 = open(file1, O_RDONLY|O_NOFOLLOW, 0)) == -1) |
1008 |
|
|
fatal("cvs_file_cmp: open: `%s': %s", file1, strerror(errno)); |
1009 |
|
|
if ((fd2 = open(file2, O_RDONLY|O_NOFOLLOW, 0)) == -1) |
1010 |
|
|
fatal("cvs_file_cmp: open: `%s': %s", file2, strerror(errno)); |
1011 |
|
|
|
1012 |
|
|
if (fstat(fd1, &stb1) == -1) |
1013 |
|
|
fatal("cvs_file_cmp: `%s': %s", file1, strerror(errno)); |
1014 |
|
|
if (fstat(fd2, &stb2) == -1) |
1015 |
|
|
fatal("cvs_file_cmp: `%s': %s", file2, strerror(errno)); |
1016 |
|
|
|
1017 |
|
|
if (stb1.st_size != stb2.st_size || |
1018 |
|
|
(stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) { |
1019 |
|
|
ret = 1; |
1020 |
|
|
goto out; |
1021 |
|
|
} |
1022 |
|
|
|
1023 |
|
|
if (S_ISBLK(stb1.st_mode) || S_ISCHR(stb1.st_mode)) { |
1024 |
|
|
if (stb1.st_rdev != stb2.st_rdev) |
1025 |
|
|
ret = 1; |
1026 |
|
|
goto out; |
1027 |
|
|
} |
1028 |
|
|
|
1029 |
|
|
if (S_ISREG(stb1.st_mode)) { |
1030 |
|
|
void *p1, *p2; |
1031 |
|
|
|
1032 |
|
|
if ((uintmax_t)stb1.st_size > SIZE_MAX) { |
1033 |
|
|
ret = 1; |
1034 |
|
|
goto out; |
1035 |
|
|
} |
1036 |
|
|
|
1037 |
|
|
if ((p1 = mmap(NULL, stb1.st_size, PROT_READ, |
1038 |
|
|
MAP_FILE, fd1, (off_t)0)) == MAP_FAILED) |
1039 |
|
|
fatal("cvs_file_cmp: mmap failed"); |
1040 |
|
|
|
1041 |
|
|
if ((p2 = mmap(NULL, stb1.st_size, PROT_READ, |
1042 |
|
|
MAP_FILE, fd2, (off_t)0)) == MAP_FAILED) |
1043 |
|
|
fatal("cvs_file_cmp: mmap failed"); |
1044 |
|
|
|
1045 |
|
|
madvise(p1, stb1.st_size, MADV_SEQUENTIAL); |
1046 |
|
|
madvise(p2, stb1.st_size, MADV_SEQUENTIAL); |
1047 |
|
|
|
1048 |
|
|
ret = memcmp(p1, p2, stb1.st_size); |
1049 |
|
|
|
1050 |
|
|
(void)munmap(p1, stb1.st_size); |
1051 |
|
|
(void)munmap(p2, stb1.st_size); |
1052 |
|
|
} |
1053 |
|
|
|
1054 |
|
|
out: |
1055 |
|
|
(void)close(fd1); |
1056 |
|
|
(void)close(fd2); |
1057 |
|
|
|
1058 |
|
|
return (ret); |
1059 |
|
|
} |
1060 |
|
|
|
1061 |
|
|
int |
1062 |
|
|
cvs_file_copy(const char *from, const char *to) |
1063 |
|
|
{ |
1064 |
|
|
struct stat st; |
1065 |
|
|
struct timeval tv[2]; |
1066 |
|
|
time_t atime, mtime; |
1067 |
|
|
int src, dst, ret; |
1068 |
|
|
|
1069 |
|
|
ret = 0; |
1070 |
|
|
|
1071 |
|
|
cvs_log(LP_TRACE, "cvs_file_copy(%s,%s)", from, to); |
1072 |
|
|
|
1073 |
|
|
if (cvs_noexec == 1) |
1074 |
|
|
return (0); |
1075 |
|
|
|
1076 |
|
|
if ((src = open(from, O_RDONLY, 0)) == -1) |
1077 |
|
|
fatal("cvs_file_copy: open: `%s': %s", from, strerror(errno)); |
1078 |
|
|
|
1079 |
|
|
if (fstat(src, &st) == -1) |
1080 |
|
|
fatal("cvs_file_copy: `%s': %s", from, strerror(errno)); |
1081 |
|
|
|
1082 |
|
|
atime = st.st_atimespec.tv_sec; |
1083 |
|
|
mtime = st.st_mtimespec.tv_sec; |
1084 |
|
|
|
1085 |
|
|
if (S_ISREG(st.st_mode)) { |
1086 |
|
|
char *p; |
1087 |
|
|
int saved_errno; |
1088 |
|
|
|
1089 |
|
|
if ((uintmax_t)st.st_size > SIZE_MAX) { |
1090 |
|
|
ret = -1; |
1091 |
|
|
goto out; |
1092 |
|
|
} |
1093 |
|
|
|
1094 |
|
|
if ((dst = open(to, O_CREAT|O_TRUNC|O_WRONLY, |
1095 |
|
|
st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) == -1) |
1096 |
|
|
fatal("cvs_file_copy: open `%s': %s", |
1097 |
|
|
to, strerror(errno)); |
1098 |
|
|
|
1099 |
|
|
if ((p = mmap(NULL, st.st_size, PROT_READ, |
1100 |
|
|
MAP_FILE, src, (off_t)0)) == MAP_FAILED) { |
1101 |
|
|
saved_errno = errno; |
1102 |
|
|
(void)unlink(to); |
1103 |
|
|
fatal("cvs_file_copy: mmap: %s", strerror(saved_errno)); |
1104 |
|
|
} |
1105 |
|
|
|
1106 |
|
|
madvise(p, st.st_size, MADV_SEQUENTIAL); |
1107 |
|
|
|
1108 |
|
|
if (atomicio(vwrite, dst, p, st.st_size) != (size_t)st.st_size) { |
1109 |
|
|
saved_errno = errno; |
1110 |
|
|
(void)unlink(to); |
1111 |
|
|
fatal("cvs_file_copy: `%s': %s", from, |
1112 |
|
|
strerror(saved_errno)); |
1113 |
|
|
} |
1114 |
|
|
|
1115 |
|
|
(void)munmap(p, st.st_size); |
1116 |
|
|
|
1117 |
|
|
tv[0].tv_sec = atime; |
1118 |
|
|
tv[1].tv_sec = mtime; |
1119 |
|
|
|
1120 |
|
|
if (futimes(dst, tv) == -1) { |
1121 |
|
|
saved_errno = errno; |
1122 |
|
|
(void)unlink(to); |
1123 |
|
|
fatal("cvs_file_copy: futimes: %s", |
1124 |
|
|
strerror(saved_errno)); |
1125 |
|
|
} |
1126 |
|
|
(void)close(dst); |
1127 |
|
|
} |
1128 |
|
|
out: |
1129 |
|
|
(void)close(src); |
1130 |
|
|
|
1131 |
|
|
return (ret); |
1132 |
|
|
} |
1133 |
|
|
|
1134 |
|
|
int |
1135 |
|
|
cvs_filelist_cmp(struct cvs_filelist *f1, struct cvs_filelist *f2) |
1136 |
|
|
{ |
1137 |
|
|
return (strcmp(f1->file_path, f2->file_path)); |
1138 |
|
|
} |