update from MirBSD; for us relevant:
[alioth/cvs.git] / src / suck.c
1 /*-
2  * Copyright (c) 2011
3  *      Thorsten Glaser <tg@mirbsd.org>
4  *
5  * Provided that these terms and disclaimer and all copyright notices
6  * are retained or reproduced in an accompanying document, permission
7  * is granted to deal in this work without restriction, including un-
8  * limited rights to use, publicly perform, distribute, sell, modify,
9  * merge, give away, or sublicence.
10  *
11  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
12  * the utmost extent permitted by applicable law, neither express nor
13  * implied; without malicious intent or gross negligence. In no event
14  * may a licensor, author or contributor be held liable for indirect,
15  * direct, other damage, loss, or other issues arising in any way out
16  * of dealing in the work, even if advised of the possibility of such
17  * damage or existence of a defect, except proven that it results out
18  * of said person's immediate fault when using the work as intended.
19  *-
20  * Download a ,v file from the repository
21  */
22
23 #include "cvs.h"
24
25 #ifdef HAVE_MMAP
26 #include <sys/mman.h>
27
28 #ifndef MAP_FILE
29 #define MAP_FILE 0
30 #endif
31
32 #ifndef MAP_FAILED
33 #define MAP_FAILED ((void *)-1)
34 #endif
35 #endif
36
37 __RCSID("$MirOS: src/gnu/usr.bin/cvs/src/suck.c,v 1.1 2011/06/11 02:57:00 tg Exp $");
38
39 static const char * const suck_usage[] = {
40         "Usage: %s %s module/filename\n",
41         NULL
42 };
43
44 int
45 suck(int argc, char *argv[])
46 {
47         size_t m, n;
48         int fd;
49         char *buf, *cp, *fn;
50         struct stat sb;
51         FILE *fp;
52         RCSNode *rcs;
53
54         if (argc != 2)
55                 usage(suck_usage);
56
57 #ifdef CLIENT_SUPPORT
58         if (current_parsed_root->isremote) {
59                 start_server();
60
61                 if (!supported_request("suck"))
62                         error(1, 0, "server does not support %s", "suck");
63
64                 send_arg(argv[1]);
65                 send_to_server("suck\012", 0);
66
67                 return (get_responses_and_close());
68         }
69 #endif
70
71         /* check for ../ attack */
72         if (pathname_levels(argv[1]) > 0)
73                 error(1, 0, "path %s outside of repository", argv[1]);
74
75         /* repo + / + module/file */
76         cp = Xasprintf("%s/%s", current_parsed_root->directory, argv[1]);
77
78         /* find the slash */
79         if ((fn = cp + (last_component(cp) - cp)) == cp)
80                 usage(suck_usage);
81
82         /* repo/module + file */
83         fn[-1] = '\0';
84
85         /* check if it's a valid RCS file, not /etc/passwd or somesuch */
86         if ((rcs = RCS_parse(fn, cp)) == NULL) {
87                 error(1, 0, "not a valid RCS file: %s/%s", cp, fn);
88                 return (1);
89         }
90
91         /* save the real pathname of the RCS file for later */
92         fn = xstrdup(rcs->path);
93
94         /* free up resources allocated until now */
95         freercsnode(&rcs);
96         free(cp);
97
98         /* attempt to open the file ourselves */
99         if ((fp = CVS_FOPEN(fn, FOPEN_BINARY_READ)) == NULL)
100                 error(1, errno, "Could not open RCS archive %s", fn);
101         if (fstat(fd = fileno(fp), &sb) < 0)
102                 error(1, errno, "Could not stat RCS archive %s", fn);
103
104         /*XXX this code will fail for large files */
105
106         /* attempt to slurp entire file into memory */
107 #ifdef HAVE_MMAP
108         buf = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
109         if (buf == NULL || buf == MAP_FAILED) {
110                 error(0, errno, "Could not map memory to RCS archive %s", fn);
111 #endif
112         /* backup: just read */
113                 cp = buf = xmalloc(n = sb.st_size);
114                 while (n) {
115                         m = read(fd, cp, n);
116                         if (m == (size_t)-1)
117                                 error(1, errno,
118                                     "Could not read RCS archive %s", fn);
119                         cp += m;
120                         n -= m;
121                 }
122 #ifdef HAVE_MMAP
123         }
124 #endif
125
126         /* write real pathname plus newline as text */
127         cvs_output(fn + strlen(current_parsed_root->directory) + 1, 0);
128         cvs_output("\n", 1);
129
130         /* write file content as binary */
131         cvs_output_binary(buf, sb.st_size);
132
133         /* release all resources allocated */
134 #ifdef HAVE_MMAP
135         munmap(buf, sb.st_size);
136 #endif
137         fclose(fp);
138         free(fn);
139
140         /* success */
141         return (0);
142 }