Bug Summary

File:obj-scan-build/libftpconn/../../libftpconn/unix.c
Location:line 61, column 15
Description:Result of 'malloc' is converted to a pointer of type 'struct sockaddr', which is incompatible with sizeof operand type 'struct sockaddr_in'

Annotated Source Code

1/* Unix-specific ftpconn hooks
2
3 Copyright (C) 1997, 1998, 2002 Free Software Foundation, Inc.
4 Written by Miles Bader <miles@gnu.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2, or (at
9 your option) any later version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include <unistd.h>
21#include <string.h>
22#include <ctype.h>
23#include <time.h>
24#include <errno(*__errno_location ()).h>
25#include <pwd.h>
26#include <grp.h>
27#include <sys/time.h>
28#include <netinet/in.h>
29#include <libgen.h> /* For dirname(). */
30#ifdef HAVE_HURD_HURD_TYPES_H1
31#include <hurd/hurd_types.h>
32#endif
33
34#include <ftpconn.h>
35
36/* Uid/gid to use when we don't know about a particular user name. */
37#define DEFAULT_UID65535 65535
38#define DEFAULT_GID65535 65535
39
40struct ftp_conn_syshooks ftp_conn_unix_syshooks = {
41 ftp_conn_unix_pasv_addr, ftp_conn_unix_interp_err,
42 ftp_conn_unix_start_get_stats, ftp_conn_unix_cont_get_stats,
43 ftp_conn_unix_append_name, ftp_conn_unix_basename
44};
45
46/* Try to get an internet address out of the reply to a PASV command.
47 Unfortunately, the format of the reply such isn't standardized. */
48error_t
49ftp_conn_unix_pasv_addr (struct ftp_conn *conn, const char *txt,
50 struct sockaddr **addr)
51{
52 unsigned a0, a1, a2, a3; /* Parts of the inet address */
53 unsigned p0, p1; /* Parts of the prot (msb, lsb) */
54
55 if (sscanf (txt, "%*[^0-9]%d,%d,%d,%d,%d,%d", &a0,&a1,&a2,&a3, &p0,&p1) != 6)
56 return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
57 else
58 {
59 unsigned char *a, *p;
60
61 *addr = malloc (sizeof (struct sockaddr_in));
Result of 'malloc' is converted to a pointer of type 'struct sockaddr', which is incompatible with sizeof operand type 'struct sockaddr_in'
62 if (! *addr)
63 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
64
65 (*addr)->sa_len = sizeof (struct sockaddr_in);
66 (*addr)->sa_family = AF_INET2;
67
68 a = (unsigned char *)&((struct sockaddr_in *)*addr)->sin_addr.s_addr;
69 a[0] = a0 & 0xff;
70 a[1] = a1 & 0xff;
71 a[2] = a2 & 0xff;
72 a[3] = a3 & 0xff;
73
74 p = (unsigned char *)&((struct sockaddr_in *)*addr)->sin_port;
75 p[0] = p0 & 0xff;
76 p[1] = p1 & 0xff;
77
78 return 0;
79 }
80}
81
82/* Compare strings P & Q in a most forgiving manner, ignoring case and
83 everything but alphanumeric characters. */
84static int
85strlaxcmp (const char *p, const char *q)
86{
87 for (;;)
88 {
89 int ch1, ch2;
90
91 while (*p && !isalnum (*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int)
_ISalnum)
)
92 p++;
93 while (*q && !isalnum (*q)((*__ctype_b_loc ())[(int) ((*q))] & (unsigned short int)
_ISalnum)
)
94 q++;
95
96 if (!*p || !*q)
97 break;
98
99 ch1 = tolower (*p);
100 ch2 = tolower (*q);
101 if (ch1 != ch2)
102 break;
103
104 p++;
105 q++;
106 }
107
108 return *p - *q;
109}
110
111/* Try to convert an error message in TXT into an error code. POSS_ERRS
112 contains a list of likely errors to try; if no other clue is found, the
113 first thing in poss_errs is returned. */
114error_t
115ftp_conn_unix_interp_err (struct ftp_conn *conn, const char *txt,
116 const error_t *poss_errs)
117{
118 const char *p;
119 const error_t *e;
120
121 if (!poss_errs || !poss_errs[0])
122 return EIO((0x10 << 26) | ((5) & 0x3fff));
123
124 /* ignore everything before the last colon. */
125 p = strrchr (txt, ':');
126 if (p)
127 p++;
128 else
129 p = txt;
130
131 /* Now, for each possible error, do a string compare ignoring case and
132 anything non-alphanumberic. */
133 for (e = poss_errs; *e; e++)
134 if (strlaxcmp (p, strerror (*e)) == 0)
135 return *e;
136
137 return poss_errs[0];
138}
139
140struct get_stats_state
141{
142 char *name; /* Last read (maybe partial) name. */
143 size_t name_len; /* Valid length of NAME, *not including* '\0'. */
144 size_t name_alloced; /* Allocated size of NAME (>= NAME_LEN). */
145 int name_partial; /* True if NAME isn't complete. */
146
147 int contents; /* Are we looking for directory contents? */
148 char *searched_name; /* If we are not, then we are only
149 looking for this name. */
150
151 int added_slash; /* Did we prefix the name with `./'? */
152
153 struct stat stat; /* Last read stat info. */
154
155 int start; /* True if at beginning of output. */
156
157 size_t buf_len; /* Length of contents in BUF. */
158 char buf[7000];
159};
160
161/* Start an operation to get a list of file-stat structures for NAME (this is
162 often similar to ftp_conn_start_dir, but with OS-specific flags), and
163 return a file-descriptor for reading on, and a state structure in STATE
164 suitable for passing to cont_get_stats. If CONTENTS is true, NAME must
165 refer to a directory, and the contents will be returned, otherwise, the
166 (single) result will refer to NAME. */
167error_t
168ftp_conn_unix_start_get_stats (struct ftp_conn *conn,
169 const char *name, int contents,
170 int *fd, void **state)
171{
172 error_t err = 0;
173 size_t req_len;
174 char *req = NULL((void*)0);
175 struct get_stats_state *s = NULL((void*)0);
176 const char *flags = "-A";
177 const char *slash = strchr (name, '/');
178 char *searched_name = NULL((void*)0);
179
180 s = (struct get_stats_state *) malloc (sizeof (struct get_stats_state));
181 if (! s)
182 {
183 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
184 goto out;
185 }
186 if (! contents)
187 {
188 if (! strcmp (name, "/"))
189 {
190 /* Listing only the directory itself and not the directory
191 content seems to be not supported by all FTP servers. If
192 the directory in question is not the root directory, we
193 can simply lookup `..', but that doesn't work if we are
194 already on the root directory. */
195 err = EINVAL((0x10 << 26) | ((22) & 0x3fff));
196 }
197 else
198 {
199 searched_name = strdup (basename__xpg_basename ((char *) name));
200 if (! searched_name)
201 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
202 }
203 if (err)
204 goto out;
205 }
206
207 if (strcspn (name, "*? \t\n{}$`\\\"'") < strlen (name))
208 /* NAME contains some metacharacters, which makes the behavior of various
209 ftp servers unpredictable, so punt. */
210 {
211 err = EINVAL((0x10 << 26) | ((22) & 0x3fff));
212 goto out;
213 }
214
215 /* We pack the ls options and the name into the list argument, in REQ,
216 which will do the right thing on most unix ftp servers. */
217
218 req_len = strlen (flags) + 2; /* space character + null character. */
219 if (! contents)
220 {
221 /* If we are looking for a directory rather than its content,
222 lookup the parent directory and search for the entry, rather
223 than looking it up directly, as not all ftp servers support
224 the -d option to ls. To make sure we get a directory, append
225 '/', except for the root directory. */
226 char *dirn = dirname (strdupa (name)(__extension__ ({ const char *__old = (name); size_t __len = strlen
(__old) + 1; char *__new = (char *) __builtin_alloca (__len)
; (char *) memcpy (__new, __old, __len); }))
);
227 int is_root = ! strcmp (dirn, "/");
228 req_len += strlen (dirn) + (is_root ? 0 : 1);
229 req = malloc (req_len);
230 if (! req)
231 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
232 else
233 sprintf (req, "%s %s%s", flags, dirn, (is_root ? "" : "/"));
234 }
235 else
236 {
237 /* If NAME doesn't contain a slash, we prepend `./' to it so that we can
238 tell from the results whether it's a directory or not. */
239 req_len += strlen (name) + (slash ? 0 : 2);
240 req = malloc (req_len);
241 if (! req)
242 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
243 else
244 sprintf (req, "%s %s%s", flags, slash ? "" : "./", name);
245 }
246
247 if (err)
248 goto out;
249
250 /* Make the actual request. */
251 err = ftp_conn_start_dir (conn, req, fd);
252
253 out:
254
255 if (req)
256 free (req);
257 if (err)
258 {
259 if (s)
260 free (s);
261 if (searched_name)
262 free (searched_name);
263 }
264 else
265 {
266 s->contents = contents;
267 s->searched_name = searched_name;
268 s->added_slash = !slash;
269 s->name = 0;
270 s->name_len = s->name_alloced = 0;
271 s->name_partial = 0;
272 s->buf_len = 0;
273 s->start = 1;
274 *state = s;
275 }
276
277 return err;
278}
279
280static char *months[] =
281{
282 "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct",
283 "nov", "dec", 0
284};
285
286/* Translate the information in the ls output in *LINE as best we can into
287 STAT, and update *LINE to point to the filename at the end of the line.
288 If *LINE should be ignored, EAGAIN is returned. */
289static error_t
290parse_dir_entry (char **line, struct stat *stat)
291{
292 char **m;
293 struct tm tm;
294 char *p = *line, *e;
295
296 /*
297drwxrwxrwt 3 root wheel 1024 May 1 16:58 /tmp
298drwxrwxrwt 5 root daemon 4096 May 1 17:15 /tmp
299drwxrwxrwt 4 root 0 1024 May 1 14:34 /tmp
300drwxrwxrwt 6 root wheel 284 May 1 12:46 /tmp
301drwxrwxrwt 4 sys sys 482 May 1 17:11 /tmp
302drwxrwxrwt 7 34 archive 512 May 1 14:28 /tmp
303 */
304
305 if (strncasecmp (p, "total ", 6) == 0)
306 return EAGAIN((0x10 << 26) | ((35) & 0x3fff));
307
308 bzero (stat, sizeof *stat);
309
310#ifdef FSTYPE_FTP0x00000005
311 stat->st_fstype = FSTYPE_FTP0x00000005;
312#endif
313
314 /* File format (S_IFMT) bits. */
315 switch (*p++)
316 {
317 case '-': stat->st_mode |= S_IFREG0100000; break;
318 case 'd': stat->st_mode |= S_IFDIR0040000; break;
319 case 'c': stat->st_mode |= S_IFCHR0020000; break;
320 case 'b': stat->st_mode |= S_IFBLK0060000; break;
321 case 'l': stat->st_mode |= S_IFLNK0120000; break;
322 case 's': stat->st_mode |= S_IFSOCK0140000; break;
323 case 'p': stat->st_mode |= S_IFIFO0010000; break;
324 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
325 }
326
327 /* User perm bits. */
328 switch (*p++)
329 {
330 case '-': break;
331 case 'r': stat->st_mode |= S_IRUSR00400; break;
332 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
333 }
334 switch (*p++)
335 {
336 case '-': break;
337 case 'w': stat->st_mode |= S_IWUSR00200; break;
338 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
339 }
340 switch (*p++)
341 {
342 case '-': break;
343 case 'x': stat->st_mode |= S_IXUSR00100; break;
344 case 's': stat->st_mode |= S_IXUSR00100 | S_ISUID04000; break;
345 case 'S': stat->st_mode |= S_ISUID04000; break;
346 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
347 }
348
349 /* Group perm bits. */
350 switch (*p++)
351 {
352 case '-': break;
353 case 'r': stat->st_mode |= S_IRGRP(00400 >> 3); break;
354 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
355 }
356 switch (*p++)
357 {
358 case '-': break;
359 case 'w': stat->st_mode |= S_IWGRP(00200 >> 3); break;
360 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
361 }
362 switch (*p++)
363 {
364 case '-': break;
365 case 'x': stat->st_mode |= S_IXGRP(00100 >> 3); break;
366 case 's': stat->st_mode |= S_IXGRP(00100 >> 3) | S_ISGID02000; break;
367 case 'S': stat->st_mode |= S_ISGID02000; break;
368 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
369 }
370
371 /* `Other' perm bits. */
372 switch (*p++)
373 {
374 case '-': break;
375 case 'r': stat->st_mode |= S_IROTH((00400 >> 3) >> 3); break;
376 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
377 }
378 switch (*p++)
379 {
380 case '-': break;
381 case 'w': stat->st_mode |= S_IWOTH((00200 >> 3) >> 3); break;
382 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
383 }
384 switch (*p++)
385 {
386 case '-': break;
387 case 'x': stat->st_mode |= S_IXOTH((00100 >> 3) >> 3); break;
388 case 't': stat->st_mode |= S_IXOTH((00100 >> 3) >> 3) | S_ISVTX01000; break;
389 case 'T': stat->st_mode |= S_ISVTX01000; break;
390 default: return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
391 }
392
393#define SKIP_WS()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
\
394 while (isspace (*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int)
_ISspace)
) p++;
395#define PARSE_INT()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
({ \
396 unsigned u = strtoul (p, &e, 10); \
397 if (e == p || isalnum (*e)((*__ctype_b_loc ())[(int) ((*e))] & (unsigned short int)
_ISalnum)
) \
398 return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff)); \
399 p = e; \
400 u; \
401 })
402
403 /* Link count. */
404 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
405 stat->st_nlink = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
406
407 /* File owner. */
408 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
409 if (isdigit (*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int)
_ISdigit)
)
410 stat->st_uid = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
411 else
412 {
413 struct passwd *pw;
414
415 e = p + strcspn (p, " \t\n");
416 *e++ = '\0';
417
418 pw = getpwnam (p);
419
420 if (pw)
421 stat->st_uid = pw->pw_uid;
422 else
423 stat->st_uid = DEFAULT_UID65535;
424
425 p = e;
426 }
427
428#ifdef HAVE_STAT_ST_AUTHOR1
429 stat->st_author = stat->st_uid;
430#endif
431
432 /* File group. */
433 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
434 if (isdigit (*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int)
_ISdigit)
)
435 stat->st_gid = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
436 else
437 {
438 struct group *gr;
439
440 e = p + strcspn (p, " \t\n");
441 *e++ = '\0';
442
443 gr = getgrnam (p);
444
445 if (gr)
446 stat->st_gid = gr->gr_gid;
447 else
448 stat->st_gid = DEFAULT_GID65535;
449
450 p = e;
451 }
452
453 /* File size / device numbers. */
454 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
455 if (S_ISCHR (stat->st_mode)((((stat->st_mode)) & 0170000) == (0020000)) || S_ISBLK (stat->st_mode)((((stat->st_mode)) & 0170000) == (0060000)))
456 /* Block and character devices show the block params instead of the file
457 size. */
458 {
459 stat->st_devst_fsid = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
460 if (*p != ',')
461 return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
462 stat->st_devst_fsid = (stat->st_devst_fsid << 8) | PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
463 stat->st_size = 0;
464 }
465 else
466 /* File size. */
467 stat->st_size = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
468
469 stat->st_blocks = stat->st_size >> 9;
470
471 /* Date. Ick. */
472 /* Formats: MONTH DAY HH:MM and MONTH DAY YEAR */
473
474 bzero (&tm, sizeof tm);
475
476 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
477 e = p + strcspn (p, " \t\n");
478 for (m = months; *m; m++)
479 if (strncasecmp (*m, p, e - p) == 0)
480 {
481 tm.tm_mon = m - months;
482 break;
483 }
484 if (! *m)
485 return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
486 p = e;
487
488 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
489 tm.tm_mday = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
490
491 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
492 if (p[1] == ':' || p[2] == ':')
493 {
494 struct tm *now_tm;
495 struct timeval now_tv;
496
497 tm.tm_hour = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
498 p++;
499 tm.tm_min = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
;
500
501 if (gettimeofday (&now_tv, 0) != 0)
502 return errno(*__errno_location ());
503
504 now_tm = localtime (&now_tv.tv_sec);
505 if (now_tm->tm_mon < tm.tm_mon)
506 tm.tm_year = now_tm->tm_year - 1;
507 else
508 tm.tm_year = now_tm->tm_year;
509 }
510 else
511 tm.tm_year = PARSE_INT ()({ unsigned u = strtoul (p, &e, 10); if (e == p || ((*__ctype_b_loc
())[(int) ((*e))] & (unsigned short int) _ISalnum)) return
((0x10 << 26) | ((105) & 0x3fff)); p = e; u; })
- 1900;
512
513 stat->st_mtim.tv_sec = mktime (&tm);
514 if (stat->st_mtim.tv_sec == (time_t)-1)
515 return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
516
517 /* atime and ctime are the same as mtime. */
518 stat->st_atim.tv_sec = stat->st_ctim.tv_sec = stat->st_mtim.tv_sec;
519 stat->st_atim.tv_nsec = stat->st_ctim.tv_nsec = stat->st_mtim.tv_nsec = 0;
520
521 /* Update *LINE to point to the filename. */
522 SKIP_WS ()while (((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short
int) _ISspace)) p++;
;
523 *line = p;
524
525 return 0;
526}
527
528/* Read stats information from FD, calling ADD_STAT for each new stat (HOOK
529 is passed to ADD_STAT). FD and STATE should be returned from
530 start_get_stats. If this function returns EAGAIN, then it should be
531 called again to finish the job (possibly after calling select on FD); if
532 it returns 0, then it is finishe,d and FD and STATE are deallocated. */
533error_t
534ftp_conn_unix_cont_get_stats (struct ftp_conn *conn, int fd, void *state,
535 ftp_conn_add_stat_fun_t add_stat, void *hook)
536{
537 char *p, *nl;
538 ssize_t rd;
539 size_t name_len;
540 error_t err = 0;
541 struct get_stats_state *s = state;
542 int (*icheck) (struct ftp_conn *conn) = conn->hooks->interrupt_check;
543
544 /* We always consume full lines, so we know that we have to read more when
545 we first get called. */
546 rd = read (fd, s->buf + s->buf_len, sizeof (s->buf) - s->buf_len);
547 if (rd < 0)
548 {
549 err = errno(*__errno_location ());
550 goto finished;
551 }
552
553 if (icheck && (*icheck) (conn))
554 {
555 err = EINTR((0x10 << 26) | ((4) & 0x3fff));
556 goto finished;
557 }
558
559 if (rd == 0)
560 /* EOF */
561 if (s->buf_len == 0)
562 /* We're done! Clean up and return the result in STATS. */
563 {
564 if (s->start)
565 /* No output at all. From many ftp servers, this means that the
566 specified file wasn't found. */
567 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
568 goto finished;
569 }
570 else
571 /* Partial line at end of file? */
572 nl = s->buf + s->buf_len;
573 else
574 /* Look for a new line in what we read (we know that there weren't any in
575 the buffer before that). */
576 {
577 nl = memchr (s->buf + s->buf_len, '\n', rd);
578 s->buf_len += rd;
579 }
580
581 s->start = 0; /* We've read past the start. */
582
583 if (!nl && s->buf_len < sizeof (s->buf))
584 /* We didn't find any newlines (which implies we didn't hit EOF), and we
585 still have room to grow the buffer, so just wait until next time to do
586 anything. */
587 return EAGAIN((0x10 << 26) | ((35) & 0x3fff));
588
589 /* Where we start parsing. */
590 p = s->buf;
591
592 do
593 {
594 if (! s->name_partial)
595 /* We aren't continuing to read an overflowed name from the previous
596 call, so we know that we are at the start of a line, and can parse
597 the info here as a directory entry. */
598 {
599 /* Parse the directory entry info, updating P to point to the
600 beginning of the name. */
601 err = parse_dir_entry (&p, &s->stat);
602 if (err == EAGAIN((0x10 << 26) | ((35) & 0x3fff)))
603 /* This line isn't a real entry and should be ignored. */
604 goto skip_line;
605 if (err)
606 goto finished;
607 }
608
609 /* Now fill in S->last_stat's name field, possibly extending it from a
610 previous buffer. */
611 name_len = (nl ? nl - p : s->buf + s->buf_len - p);
612 if (name_len > 0 && p[name_len - 1] == '\r')
613 name_len--;
614 if (name_len > 0)
615 /* Extending s->name. */
616 {
617 size_t old_len = s->name_len;
618 size_t total_len = old_len + name_len + 1;
619
620 if (total_len > s->name_alloced)
621 {
622 char *new_name = realloc (s->name, total_len);
623 if (! new_name)
624 goto enomem;
625 s->name = new_name;
626 s->name_alloced = total_len;
627 }
628
629 strncpy (s->name + old_len, p, name_len);
630 s->name[old_len + name_len] = '\0';
631 s->name_len = total_len - 1;
632 }
633
634 if (nl)
635 {
636 char *name = s->name;
637 char *symlink_target = 0;
638
639 if (S_ISLNK (s->stat.st_mode)((((s->stat.st_mode)) & 0170000) == (0120000)))
640 /* A symlink, see if we can find the link target. */
641 {
642 symlink_target = strstr (name, " -> ");
643 if (symlink_target)
644 {
645 *symlink_target = '\0';
646 symlink_target += 4;
647 }
648 }
649
650 if (strchr (name, '/'))
651 {
652 if (s->contents)
653 /* We know that the name originally request had a slash in
654 it (because we added one if necessary), so if a name in
655 the listing has one too, it can't be the contents of a
656 directory; if this is the case and we wanted the
657 contents, this must not be a directory. */
658 {
659 err = ENOTDIR((0x10 << 26) | ((20) & 0x3fff));
660 goto finished;
661 }
662 else if (s->added_slash)
663 /* S->name must be the same name we passed; if we added a
664 `./' prefix, removed it so the client gets back what it
665 passed. */
666 name += 2;
667 }
668
669 /* Pass only directory-relative names to the callback function. */
670 name = basename__xpg_basename (name);
671
672 if (s->contents || ! strcmp (s->name, s->searched_name))
673 {
674 /* We are only interested in searched_name. */
675
676 /* Call the callback function to process the current entry; it is
677 responsible for freeing S->name and SYMLINK_TARGET. */
678 err = (*add_stat) (name, &s->stat, symlink_target, hook);
679 if (err)
680 goto finished;
681 }
682
683 s->name_len = 0;
684 s->name_partial = 0;
685
686 skip_line:
687 p = nl + 1;
688 nl = memchr (p, '\n', s->buf + s->buf_len - p);
689 }
690 else
691 /* We found no newline, so the name extends past what we read; we'll
692 try to read more next time. */
693 {
694 s->name_partial = 1;
695 /* Skip over the partial name for the next iteration. */
696 p += name_len;
697 }
698 }
699 while (nl);
700
701 /* Move any remaining characters in the buffer to the beginning for the
702 next call. */
703 s->buf_len -= (p - s->buf);
704 if (s->buf_len > 0)
705 memmove (s->buf, p, s->buf_len);
706
707 /* Try again later. */
708 return EAGAIN((0x10 << 26) | ((35) & 0x3fff));
709
710enomem:
711 /* Some memory allocation failed. */
712 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
713
714finished:
715 /* We're finished (with an error if ERR != 0), deallocate everything &
716 return. */
717 if (s->name)
718 free (s->name);
719 if (s->searched_name)
720 free (s->searched_name);
721 free (s);
722 close (fd);
723
724 if (err && rd > 0)
725 ftp_conn_abort (conn);
726 else if (err)
727 ftp_conn_finish_transfer (conn);
728 else
729 err = ftp_conn_finish_transfer (conn);
730
731 return err;
732}
733
734/* Give a name which refers to a directory file, and a name in that
735 directory, this should return in COMPOSITE the composite name referring to
736 that name in that directory, in malloced storage. */
737error_t
738ftp_conn_unix_append_name (struct ftp_conn *conn,
739 const char *dir, const char *name,
740 char **composite)
741{
742 char *path = malloc (strlen (dir) + 1 + strlen (name) + 1);
743
744 if (! path)
745 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
746
747 /* Form the path name. */
748 if (name && *name)
749 if (dir[0] == '/' && dir[1] == '\0')
750 stpcpy (stpcpy (path, dir), name);
751 else
752 stpcpy (stpcpy (stpcpy (path, dir), "/"), name);
753 else
754 strcpy (path, dir);
755
756 *composite = path;
757
758 return 0;
759}
760
761/* If the name of a file *NAME is a composite name (containing both a
762 filename and a directory name), this function should change *NAME to be
763 the name component only; if the result is shorter than the original
764 *NAME, the storage pointed to it may be modified, otherwise, *NAME
765 should be changed to point to malloced storage holding the result, which
766 will be freed by the caller. */
767error_t
768ftp_conn_unix_basename (struct ftp_conn *conn, char **name)
769{
770 *name = basename__xpg_basename (*name);
771 return 0;
772}