1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18 #include "cpp.h"
19
20 #ifdef _WIN32
21 #include "boinc_win.h"
22 #ifdef _MSC_VER
23 #define snprintf _snprintf
24 #endif
25 #else
26 #include "config.h"
27 #include <cstdio>
28 #include <sys/stat.h>
29 #include <cctype>
30 #if HAVE_SYS_IPC_H
31 #include <sys/ipc.h>
32 #endif
33 #include "shmem.h"
34 #endif
35
36 #include "error_numbers.h"
37 #include "filesys.h"
38 #include "str_util.h"
39 #include "str_replace.h"
40 #include "url.h"
41 #include "util.h"
42
43 #include "client_msgs.h"
44 #include "client_state.h"
45 #include "project.h"
46 #include "sandbox.h"
47
48 #include "file_names.h"
49
make_soft_link(PROJECT * project,char * link_path,char * rel_file_path)50 int make_soft_link(PROJECT* project, char* link_path, char* rel_file_path) {
51 FILE *fp = boinc_fopen(link_path, "w");
52 if (!fp) {
53 msg_printf(project, MSG_INTERNAL_ERROR,
54 "Can't create link file %s", link_path
55 );
56 return ERR_FOPEN;
57 }
58 fprintf(fp, "<soft_link>%s</soft_link>\n", rel_file_path);
59 fclose(fp);
60 if (log_flags.slot_debug) {
61 msg_printf(project, MSG_INFO,
62 "[slot] linked %s to %s", rel_file_path, link_path
63 );
64 }
65 return 0;
66 }
67
68 // Gets the pathname of a file
69 //
get_pathname(FILE_INFO * fip,char * path,int len)70 void get_pathname(FILE_INFO* fip, char* path, int len) {
71 PROJECT* p = fip->project;
72 char buf[MAXPATHLEN];
73
74 // for testing purposes, it's handy to allow a FILE_INFO without
75 // an associated PROJECT.
76 //
77 if (p) {
78 #ifdef ENABLE_AUTO_UPDATE
79 if (fip->is_auto_update_file) {
80 boinc_version_dir(*p, gstate.auto_update.version, buf);
81 } else {
82 strcpy(buf, p->project_dir());
83 }
84 #else
85 safe_strcpy(buf, p->project_dir());
86 #endif
87 snprintf(path, len, "%s/%s", buf, fip->name);
88 } else {
89 strlcpy(path, fip->name, len);
90 }
91 }
92
get_sched_request_filename(PROJECT & project,char * buf,int len)93 void get_sched_request_filename(PROJECT& project, char* buf, int len) {
94 char url[1024];
95
96 escape_project_url(project.master_url, url);
97 snprintf(buf, len, "%s%s.xml", SCHED_OP_REQUEST_BASE, url);
98 }
99
get_sched_reply_filename(PROJECT & project,char * buf,int len)100 void get_sched_reply_filename(PROJECT& project, char* buf, int len) {
101 char url[1024];
102
103 escape_project_url(project.master_url, url);
104 snprintf(buf, len, "%s%s.xml", SCHED_OP_REPLY_BASE, url);
105 }
106
get_master_filename(PROJECT & project,char * buf,int len)107 void get_master_filename(PROJECT& project, char* buf, int len) {
108 char url[1024];
109
110 escape_project_url(project.master_url, url);
111 snprintf(buf, len, "%s%s.xml", MASTER_BASE, url);
112 }
113
job_log_filename(PROJECT & project,char * buf,int len)114 void job_log_filename(PROJECT& project, char* buf, int len) {
115 char url[1024];
116
117 escape_project_url(project.master_url, url);
118 snprintf(buf, len, "%s%s.txt", JOB_LOG_BASE, url);
119 }
120
121 // Returns the location of a numbered slot directory
122 //
get_slot_dir(int slot,char * path,int len)123 void get_slot_dir(int slot, char* path, int len) {
124 snprintf(path, len, "%s/%d", SLOTS_DIR, slot);
125 }
126
127 // Create the directory for the project p
128 //
make_project_dir(PROJECT & p)129 int make_project_dir(PROJECT& p) {
130 int retval;
131
132 boinc_mkdir(PROJECTS_DIR);
133 #ifndef _WIN32
134 mode_t old_mask;
135 if (g_use_sandbox) {
136 old_mask = umask(2); // Allow writing by group
137 chmod(PROJECTS_DIR,
138 S_IRUSR|S_IWUSR|S_IXUSR
139 |S_IRGRP|S_IWGRP|S_IXGRP
140 );
141 umask(old_mask);
142 // Only user boinc_master and group boinc_project can access
143 // project directories, to keep authenticators private
144 set_to_project_group(PROJECTS_DIR);
145 }
146 #endif
147 retval = boinc_mkdir(p.project_dir());
148 #ifndef _WIN32
149 if (g_use_sandbox) {
150 old_mask = umask(2);
151 // Contents of projects directory must be world-readable so BOINC Client can read
152 // files written by projects which have user boinc_project and group boinc_project
153 chmod(p.project_dir(),
154 S_IRUSR|S_IWUSR|S_IXUSR
155 |S_IRGRP|S_IWGRP|S_IXGRP
156 |S_IROTH|S_IXOTH
157 );
158 umask(old_mask);
159 set_to_project_group(p.project_dir());
160 }
161 #endif
162 return retval;
163 }
164
remove_project_dir(PROJECT & p)165 int remove_project_dir(PROJECT& p) {
166 int retval;
167
168 retval = client_clean_out_dir(p.project_dir(), "remove project dir");
169 if (retval) {
170 msg_printf(&p, MSG_INTERNAL_ERROR, "Can't delete file %s", boinc_failed_file);
171 return retval;
172 }
173 return remove_project_owned_dir(p.project_dir());
174 }
175
176 // Create the slot directory for the specified slot #
177 //
make_slot_dir(int slot)178 int make_slot_dir(int slot) {
179 char buf[MAXPATHLEN];
180
181 if (slot<0) {
182 msg_printf(NULL, MSG_INTERNAL_ERROR, "Bad slot number %d", slot);
183 return ERR_NEG;
184 }
185 boinc_mkdir(SLOTS_DIR);
186 #ifndef _WIN32
187 mode_t old_mask;
188 if (g_use_sandbox) {
189 old_mask = umask(2); // Allow writing by group
190 chmod(SLOTS_DIR,
191 S_IRUSR|S_IWUSR|S_IXUSR
192 |S_IRGRP|S_IWGRP|S_IXGRP
193 );
194 umask(old_mask);
195 // Only user boinc_master and group boinc_project can
196 // access slot directories, to keep authenticators private
197 set_to_project_group(SLOTS_DIR);
198 }
199
200 #endif
201 get_slot_dir(slot, buf, sizeof(buf));
202 int retval = boinc_mkdir(buf);
203 #ifndef _WIN32
204 if (g_use_sandbox) {
205 old_mask = umask(2);
206 // Contents of slots directory must be world-readable so BOINC Client can read
207 // files written by projects which have user boinc_project and group boinc_project
208 chmod(buf,
209 S_IRUSR|S_IWUSR|S_IXUSR
210 |S_IRGRP|S_IWGRP|S_IXGRP
211 |S_IROTH|S_IXOTH
212 );
213 umask(old_mask);
214 set_to_project_group(buf);
215 }
216 #endif
217 return retval;
218 }
219
220 // delete unused stuff in the slots/ directory
221 //
delete_old_slot_dirs()222 void delete_old_slot_dirs() {
223 char filename[1024], path[MAXPATHLEN];
224 DIRREF dirp;
225 int retval;
226
227 dirp = dir_open(SLOTS_DIR);
228 if (!dirp) return;
229 while (1) {
230 safe_strcpy(filename, "");
231 retval = dir_scan(filename, dirp, sizeof(filename));
232 if (retval) break;
233 snprintf(path, sizeof(path), "%s/%s", SLOTS_DIR, filename);
234 if (is_dir(path)) {
235 #ifndef _WIN32
236 #if HAVE_SYS_SHM_H
237 char init_data_path[MAXPATHLEN];
238 SHMEM_SEG_NAME shmem_seg_name;
239
240 // If BOINC crashes or exits suddenly (e.g., due to
241 // being called with --exit_after_finish) it may leave
242 // orphan shared memory segments in the system.
243 // Clean these up here. (We must do this before deleting the
244 // INIT_DATA_FILE, if any, from each slot directory.)
245 //
246 snprintf(init_data_path, sizeof(init_data_path), "%s/%s", path, INIT_DATA_FILE);
247 shmem_seg_name = ftok(init_data_path, 1);
248 if (shmem_seg_name != -1) {
249 destroy_shmem(shmem_seg_name);
250 }
251 #endif
252 #endif
253 if (!gstate.active_tasks.is_slot_dir_in_use(path)) {
254 client_clean_out_dir(path, "delete old slot dirs");
255 remove_project_owned_dir(path);
256 }
257 } else {
258 delete_project_owned_file(path, false);
259 }
260 }
261 dir_close(dirp);
262 }
263
get_account_filename(char * master_url,char * path,int len)264 void get_account_filename(char* master_url, char* path, int len) {
265 char buf[1024];
266 escape_project_url(master_url, buf);
267 snprintf(path, len, "account_%s.xml", buf);
268 }
269
bad_account_filename(const char * filename)270 static bool bad_account_filename(const char* filename) {
271 msg_printf(NULL, MSG_INTERNAL_ERROR, "Invalid account filename: %s", filename);
272 return false;
273 }
274
275 // account filenames are of the form
276 // account_URL.xml
277 // where URL is master URL with slashes replaced by underscores
278 //
is_account_file(const char * filename)279 bool is_account_file(const char* filename) {
280 const char* p, *q;
281 p = strstr(filename, "account_");
282 if (p != filename) return false;
283
284 q = filename + strlen("account_");
285 p = strstr(q, ".xml");
286 if (!p) return bad_account_filename(filename);
287 if (p == q) return bad_account_filename(filename);
288
289 q = p + strlen(".xml");
290 if (strlen(q)) return bad_account_filename(filename);
291 return true;
292 }
293
294 // statistics filenames are of the form
295 // statistics_URL.xml
296 // where URL is master URL with slashes replaced by underscores
297 //
is_statistics_file(const char * filename)298 bool is_statistics_file(const char* filename) {
299 const char* p, *q;
300 p = strstr(filename, "statistics_");
301 if (p != filename) return false;
302 q = filename + strlen("statistics_");
303
304 p = strstr(q, ".");
305 if (!p) return bad_account_filename(filename);
306 if (p == q) return bad_account_filename(filename);
307
308 q = p+1;
309 p = strstr(q, ".xml");
310 if (!p) return bad_account_filename(filename);
311 if (p == q) return bad_account_filename(filename);
312
313 q = p + strlen(".xml");
314 if (strlen(q)) return bad_account_filename(filename);
315 return true;
316 }
317
get_statistics_filename(char * master_url,char * path,int len)318 void get_statistics_filename(char* master_url, char* path, int len) {
319 char buf[256];
320 escape_project_url(master_url, buf);
321 snprintf(path, len, "statistics_%s.xml", buf);
322 }
323
is_image_file(const char * filename)324 bool is_image_file(const char* filename) {
325 string fn = filename;
326 downcase_string(fn);
327 if (ends_with(fn, string(".jpg"))) return true;
328 if (ends_with(fn, string(".jpeg"))) return true;
329 if (ends_with(fn, string(".png"))) return true;
330 return false;
331 }
332
boinc_version_dir(PROJECT & p,VERSION_INFO & vi,char * buf)333 void boinc_version_dir(PROJECT& p, VERSION_INFO& vi, char* buf) {
334 sprintf(buf, "%s/boinc_version_%d_%d_%d",
335 p.project_dir(), vi.major, vi.minor, vi.release
336 );
337 }
338
is_version_dir(char * buf,VERSION_INFO & vi)339 bool is_version_dir(char* buf, VERSION_INFO& vi) {
340 int n = sscanf(buf, "boinc_version_%d_%d_%d", &vi.major, &vi.minor, &vi.release);
341 return (n==3);
342 }
343
344