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