File: | obj-scan-build/ftpfs/../../ftpfs/ftpfs.c |
Location: | line 244, column 20 |
Description: | Null pointer passed as an argument to a 'nonnull' parameter |
1 | /* Ftp filesystem | |||
2 | ||||
3 | Copyright (C) 1997,98,2002 Free Software Foundation, Inc. | |||
4 | Written by Miles Bader <miles@gnu.org> | |||
5 | This file is part of the GNU Hurd. | |||
6 | ||||
7 | The GNU Hurd is free software; you can redistribute it and/or | |||
8 | modify it under the terms of the GNU General Public License as | |||
9 | published by the Free Software Foundation; either version 2, or (at | |||
10 | your option) any later version. | |||
11 | ||||
12 | The GNU Hurd is distributed in the hope that it will be useful, but | |||
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
15 | General Public License for more details. | |||
16 | ||||
17 | You should have received a copy of the GNU General Public License | |||
18 | along with this program; if not, write to the Free Software | |||
19 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ | |||
20 | ||||
21 | #include <string.h> | |||
22 | #include <unistd.h> | |||
23 | #include <argp.h> | |||
24 | #include <error.h> | |||
25 | #include <argz.h> | |||
26 | #include <netdb.h> | |||
27 | #include <sys/stat.h> | |||
28 | ||||
29 | #include <version.h> | |||
30 | ||||
31 | #include <hurd/netfs.h> | |||
32 | ||||
33 | #include "ftpfs.h" | |||
34 | ||||
35 | char *netfs_server_name = "ftpfs"; | |||
36 | char *netfs_server_version = HURD_VERSION"0.5"; | |||
37 | ||||
38 | const char *argp_program_version = STANDARD_HURD_VERSION (ftpfs)"ftpfs" " (GNU Hurd) " "0.5"; | |||
39 | ||||
40 | static char *args_doc = "REMOTE_FS [SERVER]"; | |||
41 | static char *doc = "Hurd ftp filesystem translator." | |||
42 | "\vIf SERVER is not specified, an attempt is made to extract" | |||
43 | " it from REMOTE_FS, using `SERVER:FS' notation." | |||
44 | " SERVER can be a hostname, in which case anonymous ftp is used," | |||
45 | " or may include a user and password like `USER:PASSWORD@HOST' (the" | |||
46 | " `:PASSWORD' part is optional)."; | |||
47 | ||||
48 | /* The filesystem. */ | |||
49 | struct ftpfs *ftpfs; | |||
50 | ||||
51 | /* Parameters describing the server we're connecting to. */ | |||
52 | struct ftp_conn_params *ftpfs_ftp_params = 0; | |||
53 | ||||
54 | /* customization hooks. */ | |||
55 | struct ftp_conn_hooks ftpfs_ftp_hooks = { interrupt_check: ports_self_interrupted }; | |||
56 | ||||
57 | /* The (user-specified) name of the SERVER:FILESYSTEM we're connected too. */ | |||
58 | char *ftpfs_remote_fs; | |||
59 | ||||
60 | /* The FILESYSTEM component of FTPFS_REMOTE_FS. */ | |||
61 | char *ftpfs_remote_root; | |||
62 | ||||
63 | /* Random parameters for the filesystem. */ | |||
64 | struct ftpfs_params ftpfs_params; | |||
65 | ||||
66 | volatile struct mapped_time_value *ftpfs_maptime; | |||
67 | ||||
68 | int netfs_maxsymlinks = 12; | |||
69 | ||||
70 | extern error_t lookup_server (const char *server, | |||
71 | struct ftp_conn_params **params, int *h_err); | |||
72 | ||||
73 | static FILE *debug_stream = 0; | |||
74 | static char *debug_stream_name = 0; | |||
75 | static pthread_mutex_t debug_lock = PTHREAD_MUTEX_INITIALIZER{ ((__pthread_spinlock_t) 0), ((__pthread_spinlock_t) 0), 0, 0 , 0, 0, 0, 0 }; | |||
76 | ||||
77 | /* Prints ftp connection log to DEBUG_STREAM. */ | |||
78 | static void | |||
79 | cntl_debug (struct ftp_conn *conn, int type, const char *txt) | |||
80 | { | |||
81 | char *type_str; | |||
82 | ||||
83 | switch (type) | |||
84 | { | |||
85 | case FTP_CONN_CNTL_DEBUG_CMD1: type_str = ">"; break; | |||
86 | case FTP_CONN_CNTL_DEBUG_REPLY2: type_str = "="; break; | |||
87 | default: type_str = "?"; break; | |||
88 | } | |||
89 | ||||
90 | pthread_mutex_lock (&debug_lock); | |||
91 | if (debug_stream) | |||
92 | { | |||
93 | fprintf (debug_stream, "%u.%s%s\n", | |||
94 | (unsigned)(uintptr_t)conn->hook, type_str, txt); | |||
95 | fflush (debug_stream); | |||
96 | } | |||
97 | pthread_mutex_unlock (&debug_lock); | |||
98 | } | |||
99 | ||||
100 | /* Various default parameters. */ | |||
101 | #define DEFAULT_NAME_TIMEOUT300 300 | |||
102 | #define DEFAULT_STAT_TIMEOUT120 120 | |||
103 | ||||
104 | #define DEFAULT_BULK_STAT_PERIOD10 10 | |||
105 | #define DEFAULT_BULK_STAT_THRESHOLD5 5 | |||
106 | ||||
107 | #define DEFAULT_NODE_CACHE_MAX50 50 | |||
108 | ||||
109 | /* Return a string corresponding to the printed rep of DEFAULT_what */ | |||
110 | #define ___D(what)"what" #what | |||
111 | #define __D(what)"what" ___D(what)"what" | |||
112 | #define _D(what)"DEFAULT_what" __D(DEFAULT_ ## what)"DEFAULT_ ## what" | |||
113 | ||||
114 | /* Common (runtime & startup) options. */ | |||
115 | ||||
116 | #define OPT_NO_DEBUG1 1 | |||
117 | ||||
118 | #define OPT_NAME_TIMEOUT5 5 | |||
119 | #define OPT_STAT_TIMEOUT7 7 | |||
120 | #define OPT_NODE_CACHE_MAX8 8 | |||
121 | #define OPT_BULK_STAT_PERIOD9 9 | |||
122 | #define OPT_BULK_STAT_THRESHOLD10 10 | |||
123 | ||||
124 | /* Options usable both at startup and at runtime. */ | |||
125 | static const struct argp_option common_options[] = | |||
126 | { | |||
127 | {"debug", 'D', "FILE", OPTION_ARG_OPTIONAL0x1, "Print debug output to FILE"}, | |||
128 | {"no-debug", OPT_NO_DEBUG1, 0, OPTION_HIDDEN0x2 }, | |||
129 | ||||
130 | {0,0,0,0, "Parameters:"}, | |||
131 | {"name-timeout", OPT_NAME_TIMEOUT5, "SECS", 0, | |||
132 | "Time directory names are cached (default " _D(NAME_TIMEOUT)"300" ")"}, | |||
133 | {"stat-timeout", OPT_STAT_TIMEOUT7, "SECS", 0, | |||
134 | "Time stat information is cached (default " _D(STAT_TIMEOUT)"120" ")"}, | |||
135 | {"node-cache-size", OPT_NODE_CACHE_MAX8, "ENTRIES", 0, | |||
136 | "Number of recently used filesystem nodes that are cached (default " | |||
137 | _D(NODE_CACHE_MAX)"50" ")"}, | |||
138 | ||||
139 | {"bulk-stat-period", OPT_BULK_STAT_PERIOD9, "SECS", 0, | |||
140 | "Period for detecting bulk stats (default " _D(BULK_STAT_PERIOD)"10" ")"}, | |||
141 | {"bulk-stat-threshold", OPT_BULK_STAT_THRESHOLD10, "SECS", 0, | |||
142 | "Number of stats within the bulk-stat-period that trigger a bulk stat" | |||
143 | " (default " _D(BULK_STAT_THRESHOLD)"5" ")"}, | |||
144 | ||||
145 | {0, 0} | |||
146 | }; | |||
147 | ||||
148 | static error_t | |||
149 | parse_common_opt (int key, char *arg, struct argp_state *state) | |||
150 | { | |||
151 | error_t err = 0; | |||
152 | struct ftpfs_params *params = state->input; | |||
153 | ||||
154 | switch (key) | |||
155 | { | |||
156 | case 'D': | |||
157 | pthread_mutex_lock (&debug_lock); | |||
158 | ||||
159 | if (debug_stream && debug_stream != stderrstderr) | |||
160 | fclose (debug_stream); | |||
161 | if (debug_stream_name) | |||
162 | { | |||
163 | free (debug_stream_name); | |||
164 | debug_stream_name = 0; | |||
165 | } | |||
166 | ||||
167 | if (arg) | |||
168 | { | |||
169 | debug_stream_name = strdup (arg); | |||
170 | if (! debug_stream_name) | |||
171 | { | |||
172 | argp_failure (state, 0, ENOMEM((0x10 << 26) | ((12) & 0x3fff)), "%s: Cannot open debugging file", arg); | |||
173 | err = ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
174 | } | |||
175 | ||||
176 | if (! err) | |||
177 | { | |||
178 | debug_stream = fopen (arg, "w+"); | |||
179 | if (! debug_stream) | |||
180 | { | |||
181 | err = errno(*__errno_location ()); | |||
182 | argp_failure (state, 0, err, "%s: Cannot open debugging file", arg); | |||
183 | } | |||
184 | } | |||
185 | } | |||
186 | else | |||
187 | debug_stream = stderrstderr; | |||
188 | ||||
189 | if (! err) | |||
190 | ftpfs_ftp_hooks.cntl_debug = cntl_debug; | |||
191 | ||||
192 | pthread_mutex_unlock (&debug_lock); | |||
193 | ||||
194 | return err; | |||
195 | ||||
196 | case OPT_NO_DEBUG1: | |||
197 | pthread_mutex_lock (&debug_lock); | |||
198 | if (debug_stream && debug_stream != stderrstderr) | |||
199 | fclose (debug_stream); | |||
200 | ftpfs_ftp_hooks.cntl_debug = 0; | |||
201 | pthread_mutex_unlock (&debug_lock); | |||
202 | break; | |||
203 | ||||
204 | case OPT_NODE_CACHE_MAX8: | |||
205 | params->node_cache_max = atoi (arg); break; | |||
206 | case OPT_NAME_TIMEOUT5: | |||
207 | params->name_timeout = atoi (arg); break; | |||
208 | case OPT_STAT_TIMEOUT7: | |||
209 | params->stat_timeout = atoi (arg); break; | |||
210 | default: | |||
211 | return ARGP_ERR_UNKNOWN((0x10 << 26) | ((7) & 0x3fff)); | |||
212 | } | |||
213 | ||||
214 | return 0; | |||
215 | } | |||
216 | ||||
217 | static struct argp common_argp = { common_options, parse_common_opt }; | |||
218 | ||||
219 | /* Startup options. */ | |||
220 | ||||
221 | static const struct argp_option startup_options[] = | |||
222 | { | |||
223 | { 0 } | |||
224 | }; | |||
225 | ||||
226 | /* Parse a single command line option/argument. */ | |||
227 | static error_t | |||
228 | parse_startup_opt (int key, char *arg, struct argp_state *state) | |||
229 | { | |||
230 | switch (key) | |||
| ||||
231 | { | |||
232 | case ARGP_KEY_ARG0: | |||
233 | if (state->arg_num > 1) | |||
234 | argp_usage (state); | |||
235 | else if (state->arg_num == 0) | |||
236 | ftpfs_remote_fs = arg; | |||
237 | else | |||
238 | /* If the fs & server are two separate args, glom them together into the | |||
239 | ":" notation. */ | |||
240 | { | |||
241 | char *rfs = malloc (strlen (ftpfs_remote_fs) + 1 + strlen (arg) + 1); | |||
242 | if (! rfs) | |||
243 | argp_failure (state, 99, ENOMEM((0x10 << 26) | ((12) & 0x3fff)), "%s", arg); | |||
244 | stpcpy (stpcpy (stpcpy (rfs, arg), ":"), ftpfs_remote_fs); | |||
| ||||
245 | ftpfs_remote_fs = rfs; | |||
246 | } | |||
247 | break; | |||
248 | ||||
249 | case ARGP_KEY_SUCCESS0x1000004: | |||
250 | /* Validate the remote fs arg; at this point FTPFS_REMOTE_FS is in | |||
251 | SERVER:FS notation. */ | |||
252 | if (state->arg_num == 0) | |||
253 | argp_error (state, "No remote filesystem specified"); | |||
254 | else | |||
255 | { | |||
256 | int h_err; /* Host lookup error. */ | |||
257 | error_t err; | |||
258 | char *sep = strrchr (ftpfs_remote_fs, '@'); | |||
259 | ||||
260 | if (sep) | |||
261 | /* FTPFS_REMOTE_FS includes a '@', which means that it's in | |||
262 | USER[:PASS]@HOST:FS notation, so we have to be careful not to | |||
263 | choose the wrong `:' as the SERVER-FS separator. */ | |||
264 | sep = strchr (sep, ':'); | |||
265 | else | |||
266 | sep = strchr (ftpfs_remote_fs, ':'); | |||
267 | ||||
268 | if (! sep) | |||
269 | /* We have just a host name, so treat it as "HOST:/". */ | |||
270 | ftpfs_remote_root = "/"; | |||
271 | else | |||
272 | ftpfs_remote_root = sep + 1; | |||
273 | ||||
274 | /* Lookup the ftp server (the part before the `:'). */ | |||
275 | if (sep) | |||
276 | *sep = '\0'; | |||
277 | err = lookup_server (ftpfs_remote_fs, &ftpfs_ftp_params, &h_err); | |||
278 | if (err == EINVAL((0x10 << 26) | ((22) & 0x3fff))) | |||
279 | argp_failure (state, 10, 0, "%s: %s", | |||
280 | ftpfs_remote_fs, hstrerror (h_err)); | |||
281 | else if (err) | |||
282 | argp_failure (state, 11, err, "%s", ftpfs_remote_fs); | |||
283 | if (sep) | |||
284 | *sep = ':'; | |||
285 | } | |||
286 | ||||
287 | case ARGP_KEY_INIT0x1000003: | |||
288 | /* Setup up state for our first child parser (common options). */ | |||
289 | state->child_inputs[0] = &ftpfs_params; | |||
290 | break; | |||
291 | ||||
292 | default: | |||
293 | return ARGP_ERR_UNKNOWN((0x10 << 26) | ((7) & 0x3fff)); | |||
294 | } | |||
295 | ||||
296 | return 0; | |||
297 | } | |||
298 | ||||
299 | /* Runtime options. */ | |||
300 | ||||
301 | /* Parse a single command line option/argument. */ | |||
302 | static error_t | |||
303 | parse_runtime_opt (int key, char *arg, struct argp_state *state) | |||
304 | { | |||
305 | if (key == ARGP_KEY_INIT0x1000003) | |||
306 | /* Setup up state for our first child parser (common options). */ | |||
307 | { | |||
308 | state->child_inputs[0] = &ftpfs->params; | |||
309 | return 0; | |||
310 | } | |||
311 | else | |||
312 | return ARGP_ERR_UNKNOWN((0x10 << 26) | ((7) & 0x3fff)); | |||
313 | } | |||
314 | ||||
315 | static const struct argp_child runtime_argp_children[] = | |||
316 | { {&common_argp}, {&netfs_std_runtime_argp}, {0} }; | |||
317 | static struct argp runtime_argp = | |||
318 | { 0, parse_runtime_opt, 0, 0, runtime_argp_children }; | |||
319 | ||||
320 | /* Use by netfs_set_options to handle runtime option parsing. */ | |||
321 | struct argp *netfs_runtime_argp = &runtime_argp; | |||
322 | ||||
323 | /* Return an argz string describing the current options. Fill *ARGZ | |||
324 | with a pointer to newly malloced storage holding the list and *LEN | |||
325 | to the length of that storage. */ | |||
326 | error_t | |||
327 | netfs_append_args (char **argz, size_t *argz_len) | |||
328 | { | |||
329 | char buf[80]; | |||
330 | error_t err = 0; | |||
331 | ||||
332 | #define FOPT(fmt, arg)do { if (! err) { snprintf (buf, sizeof buf, fmt, arg); err = argz_add (argz, argz_len, buf); } } while (0) \ | |||
333 | do { \ | |||
334 | if (! err) \ | |||
335 | { \ | |||
336 | snprintf (buf, sizeof buf, fmt, arg); \ | |||
337 | err = argz_add (argz, argz_len, buf); \ | |||
338 | } \ | |||
339 | } while (0) | |||
340 | ||||
341 | pthread_mutex_lock (&debug_lock); | |||
342 | if (ftpfs_ftp_hooks.cntl_debug && debug_stream) | |||
343 | { | |||
344 | if (debug_stream != stderrstderr) | |||
345 | { | |||
346 | char *rep; | |||
347 | asprintf (&rep, "--debug=%s", debug_stream_name); | |||
348 | err = argz_add (argz, argz_len, rep); | |||
349 | free (rep); | |||
350 | } | |||
351 | else | |||
352 | err = argz_add (argz, argz_len, "--debug"); | |||
353 | } | |||
354 | pthread_mutex_unlock (&debug_lock); | |||
355 | ||||
356 | if (ftpfs->params.name_timeout != DEFAULT_NAME_TIMEOUT300) | |||
357 | FOPT ("--name-timeout=%ld", ftpfs->params.name_timeout)do { if (! err) { snprintf (buf, sizeof buf, "--name-timeout=%ld" , ftpfs->params.name_timeout); err = argz_add (argz, argz_len , buf); } } while (0); | |||
358 | if (ftpfs->params.stat_timeout != DEFAULT_STAT_TIMEOUT120) | |||
359 | FOPT ("--stat-timeout=%ld", ftpfs->params.stat_timeout)do { if (! err) { snprintf (buf, sizeof buf, "--stat-timeout=%ld" , ftpfs->params.stat_timeout); err = argz_add (argz, argz_len , buf); } } while (0); | |||
360 | if (ftpfs->params.node_cache_max != DEFAULT_NODE_CACHE_MAX50) | |||
361 | FOPT ("--node-cache-size=%Zu", ftpfs->params.node_cache_max)do { if (! err) { snprintf (buf, sizeof buf, "--node-cache-size=%Zu" , ftpfs->params.node_cache_max); err = argz_add (argz, argz_len , buf); } } while (0); | |||
362 | if (ftpfs->params.bulk_stat_period != DEFAULT_BULK_STAT_PERIOD10) | |||
363 | FOPT ("--bulk-stat-period=%ld", ftpfs->params.bulk_stat_period)do { if (! err) { snprintf (buf, sizeof buf, "--bulk-stat-period=%ld" , ftpfs->params.bulk_stat_period); err = argz_add (argz, argz_len , buf); } } while (0); | |||
364 | if (ftpfs->params.bulk_stat_threshold != DEFAULT_BULK_STAT_THRESHOLD5) | |||
365 | FOPT ("--bulk-stat-threshold=%d", ftpfs->params.bulk_stat_threshold)do { if (! err) { snprintf (buf, sizeof buf, "--bulk-stat-threshold=%d" , ftpfs->params.bulk_stat_threshold); err = argz_add (argz , argz_len, buf); } } while (0); | |||
366 | ||||
367 | return argz_add (argz, argz_len, ftpfs_remote_fs); | |||
368 | } | |||
369 | ||||
370 | /* Program entry point. */ | |||
371 | int | |||
372 | main (int argc, char **argv) | |||
373 | { | |||
374 | error_t err; | |||
375 | mach_port_t bootstrap, underlying_node; | |||
376 | struct stat underlying_stat; | |||
377 | const struct argp_child argp_children[] = | |||
378 | { {&common_argp}, {&netfs_std_startup_argp}, {0} }; | |||
379 | struct argp argp = | |||
380 | { startup_options, parse_startup_opt, args_doc, doc, argp_children }; | |||
381 | ||||
382 | ftpfs_params.name_timeout = DEFAULT_NAME_TIMEOUT300; | |||
383 | ftpfs_params.stat_timeout = DEFAULT_STAT_TIMEOUT120; | |||
384 | ftpfs_params.node_cache_max = DEFAULT_NODE_CACHE_MAX50; | |||
385 | ftpfs_params.bulk_stat_period = DEFAULT_BULK_STAT_PERIOD10; | |||
386 | ftpfs_params.bulk_stat_threshold = DEFAULT_BULK_STAT_THRESHOLD5; | |||
387 | ||||
388 | argp_parse (&argp, argc, argv, 0, 0, 0); | |||
389 | ||||
390 | task_get_bootstrap_port (mach_task_self (), &bootstrap)(task_get_special_port((((__mach_task_self_ + 0))), 4, (& bootstrap))); | |||
391 | ||||
392 | netfs_init (); | |||
393 | ||||
394 | err = maptime_map (0, 0, &ftpfs_maptime); | |||
395 | if (err) | |||
396 | error (3, err, "mapping time"); | |||
397 | ||||
398 | err = ftpfs_create (ftpfs_remote_root, getpid (), | |||
399 | ftpfs_ftp_params, &ftpfs_ftp_hooks, | |||
400 | &ftpfs_params, &ftpfs); | |||
401 | if (err) | |||
402 | error (4, err, "%s", ftpfs_remote_fs); | |||
403 | ||||
404 | netfs_root_node = ftpfs->root; | |||
405 | ||||
406 | underlying_node = netfs_startup (bootstrap, 0); | |||
407 | err = io_stat (underlying_node, &underlying_stat); | |||
408 | if (err) | |||
409 | error (1, err, "cannot stat underling node"); | |||
410 | ||||
411 | /* Initialize stat information of the root node. */ | |||
412 | netfs_root_node->nn_stat = underlying_stat; | |||
413 | netfs_root_node->nn_stat.st_mode = | |||
414 | S_IFDIR0040000 | (underlying_stat.st_mode & ~S_IFMT0170000 & ~S_ITRANS000070000000); | |||
415 | ||||
416 | /* If the underlying node isn't a directory, propagate read permission to | |||
417 | execute permission since we need that for lookups. */ | |||
418 | if (! S_ISDIR (underlying_stat.st_mode)((((underlying_stat.st_mode)) & 0170000) == (0040000))) | |||
419 | { | |||
420 | if (underlying_stat.st_mode & S_IRUSR00400) | |||
421 | netfs_root_node->nn_stat.st_mode |= S_IXUSR00100; | |||
422 | if (underlying_stat.st_mode & S_IRGRP(00400 >> 3)) | |||
423 | netfs_root_node->nn_stat.st_mode |= S_IXGRP(00100 >> 3); | |||
424 | if (underlying_stat.st_mode & S_IROTH((00400 >> 3) >> 3)) | |||
425 | netfs_root_node->nn_stat.st_mode |= S_IXOTH((00100 >> 3) >> 3); | |||
426 | } | |||
427 | ||||
428 | for (;;) | |||
429 | netfs_server_loop (); | |||
430 | } |