1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <limits.h>
5 #include <sys/param.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <syslog.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <pwd.h>
13
14 #include "smbftpd.h"
15 #include "pathnames.h"
16
17 #ifndef LINE_MAX
18 #define LINE_MAX 2048
19 #endif
20
21 extern smbftpd_session_t smbftpd_session;
22
23 /**
24 * Get the real full path on the filesystem. If the path is a symbolic
25 * link, we will chdir to the path and use getcwd to get the realpath.
26 *
27 * We need to make sure we are not using symbolic path in the share's
28 * path. Otherwise, it will failed in smbftpd_get_realpath()
29 *
30 * @param path
31 *
32 * @return
33 */
real_full_path(const char * path)34 static char *real_full_path(const char *path)
35 {
36 static char real_path[PATH_MAX];
37 char curpath[PATH_MAX];
38
39 bzero(real_path, sizeof(real_path));
40 bzero(curpath, sizeof(curpath));
41
42 getcwd(curpath, sizeof(curpath));
43 if (chdir(path) == 0) {
44 getcwd(real_path, sizeof(real_path));
45 chdir(curpath);
46 return real_path;
47 } else {
48 return NULL;
49 }
50 }
51
52 /**
53 * Find the share in validshares and return its path.
54 *
55 * @param validshares
56 * The linked list of all valid shares
57 * @param share The share name to look for
58 *
59 * @return Return path of share when the share is found in the validshares.
60 * Return NULL if not found.
61 */
smbftpd_share_path_get(smbftpd_valid_share_t * validshares,const char * share)62 static char *smbftpd_share_path_get(smbftpd_valid_share_t *validshares, const char *share)
63 {
64 smbftpd_valid_share_t *p;
65 char *path = NULL;
66
67 if ((NULL == validshares) || (NULL == share)){
68 syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
69 return NULL;
70 }
71
72 p = validshares;
73 while (p) {
74 if (strcmp(share, p->share) == 0) {
75 path = p->path;
76 break;
77 }
78 p = p->next;
79 }
80 return path;
81 }
82
83 /**
84 * Check whether the path is one of the share path root in the
85 * validshares.
86 *
87 * We will traverse the validshares and compare the share path with path.
88 * When the path is the same, reture 1.
89 *
90 * @param validshares
91 * The linked list of all valid shares
92 * @param path The path to search
93 *
94 * @return 1: The path is a share root path
95 * 0: The path is not a share root path
96 */
is_share_root(smbftpd_valid_share_t * validshares,const char * path)97 static int is_share_root(smbftpd_valid_share_t *validshares, const char *path)
98 {
99 smbftpd_valid_share_t *p;
100
101 if (NULL == path){
102 syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
103 return 0;
104 }
105
106 p = validshares;
107 while (p) {
108 if (strcmp(path, p->path) == 0) {
109 return 1;
110 }
111 p = p->next;
112 }
113 return 0;
114 }
115
116 /**
117 * This function will replace the real path name to virtual path name
118 * in the given path.
119 *
120 * For example, if share "SHARE" has real path named "/volume1/share1",
121 * when user access path "/volume1/share1/abc/cde", we will replace the
122 * path to "/SHARE/abc/cde".
123 *
124 * @param validshares
125 * The linked list of valid shares
126 * @param path The current path. We will compare the path with path in the
127 * validshares. If path matches, we will write new path in "path"
128 * @param bufsize The buffer length of path
129 *
130 * @return 0: Success
131 * -1: Failed or path not found in validshares
132 */
smbfptd_replace_share_path(smbftpd_valid_share_t * validshares,char * path,int bufsize)133 int smbfptd_replace_share_path(smbftpd_valid_share_t *validshares, char *path, int bufsize)
134 {
135 smbftpd_valid_share_t *p;
136 char *slash;
137 char buf[PATH_MAX];
138
139 if ( (path == NULL) || (bufsize == 0) ) {
140 syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
141 return -1;
142 }
143
144 if (smbftpd_session.mode == MODE_NORMAL) {
145 return 0;
146 }
147
148 if (strcmp(path, PATH_SMB_FTPD_ROOT) == 0) {
149 snprintf(path, bufsize, "/");
150 return 0;
151 }
152
153 snprintf(buf, sizeof(buf), "%s", path);
154
155 p = validshares;
156 while (p) {
157
158 if (strncmp(p->path, path, strlen(p->path)) == 0) {
159 slash = buf + strlen(p->path);
160 if (*slash != '/' && *slash != '\0') {
161 continue;
162 }
163
164 snprintf(path, bufsize, "/%s%s", p->share, slash);
165
166 return 0;
167 }
168 p = p->next;
169 }
170
171 return -1;
172 }
173
174 /**
175 * Get the pointer of smbftpd_valid_share_t from the given
176 * validshares by path.
177 *
178 * The path is a real full path. We will compare the path with path of
179 * all shares in validshares. If path matches, reture the pointer of
180 * the smbftpd_valid_share_t
181 *
182 * @param validshares
183 * The linked list of valid shares
184 * @param path Real full path to check
185 *
186 * @return NULL if no match share
187 * If found, return the valid share
188 *
189 * Please note the returned valid share is a const pointer. It
190 * is pointed to smbftpd_session.valid_shares.
191 */
smbftpd_get_share_by_path(smbftpd_valid_share_t * validshares,const char * path)192 const smbftpd_valid_share_t *smbftpd_get_share_by_path(smbftpd_valid_share_t *validshares, const char *path)
193 {
194 smbftpd_valid_share_t *p;
195 const char *slash;
196
197 // for each share, compare the path with the share path
198 for (p = validshares; p; p = p->next) {
199 if (!p->path) {
200 continue;
201 }
202 if (strncmp(p->path, path, strlen(p->path)) == 0) {
203 slash = path + strlen(p->path);
204 if (*slash != '/' && *slash != '\0') {
205 continue;
206 }
207 return p;
208 }
209 }
210 return NULL;
211 }
212
213 /**
214 * Check whether szPath is a valid path. We will go through the
215 * validshares and check whenther the path is under valid shares.
216 * When writable is not 0, we will also check the share must be
217 * writable.
218 *
219 * @param validshares
220 * The linked list of valid shares
221 * @param path The path to check
222 * @param writable Check whether the share is writable
223 *
224 * @return 1: Yes
225 * 0: No or failed
226 */
smbftpd_is_under_valid_path(smbftpd_valid_share_t * validshares,const char * path,int writable)227 static int smbftpd_is_under_valid_path(smbftpd_valid_share_t *validshares, const char *path, int writable)
228 {
229 const smbftpd_valid_share_t *share;
230
231 if (!path) {
232 syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
233 return 0;
234 }
235
236 share = smbftpd_get_share_by_path(validshares, path);
237 if (!share) {
238 return 0;
239 }
240 if (writable && !share->writable) {
241 return 0;
242 }
243
244 return 1;
245 }
246
247 /**
248 * This is the main path parse function.
249 *
250 * The function will return the real path in the system by parse
251 * the given path. We will chdir() into each component in the
252 * given path to make sure the path is valid.
253 *
254 * If flags & FLAG_CHECK_WRITABLE, than check whether path is writable.
255 * If flags & FLAG_NO_FOLLOW_LINK, then do not follow the link.
256 * If flags & FLAG_NO_FOLLOW_LAST_LINK, then we won't check the last
257 * component.
258 *
259 * @param validshares
260 * The linked list of valid shares
261 * @param path The path to convert
262 * @param flags If flags & FLAG_CHECK_WRITABLE, than check whether path is writable.
263 * If flags & FLAG_NO_FOLLOW_LINK, then do not follow the link.
264 * If flags & FLAG_NO_FOLLOW_LAST_LINK, then we won't check the last
265 * component.
266 *
267 * @return Real path if the path is valid.
268 * NULL if the path is not allowed
269 */
smbftpd_get_realpath(smbftpd_valid_share_t * validshares,const char * path,int flags)270 char *smbftpd_get_realpath(smbftpd_valid_share_t *validshares, const char *path, int flags)
271 {
272 static char return_path[PATH_MAX+1];
273 char orig_pwd[PATH_MAX+1], curdir[PATH_MAX+1];
274 char token_path[PATH_MAX+1], tmp_path[PATH_MAX+1];
275 char *token;
276 struct stat statbuf;
277 int err;
278
279 if (!path) {
280 syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
281 return NULL;
282 }
283
284 if (smbftpd_session.mode == MODE_NORMAL) {
285 if (flags & FLAG_NO_FOLLOW_LAST_LINK) {
286 strcpy(return_path, path);
287 } else {
288 realpath(path, return_path);
289 }
290
291 return return_path;
292 }
293
294 if (*path == '\0') {
295 return NULL;
296 }
297
298 bzero(tmp_path, sizeof(tmp_path));
299 snprintf(tmp_path, sizeof(tmp_path), "%s", path);
300
301 REDO:
302 bzero((void *)&statbuf, sizeof(statbuf));
303 bzero(return_path, sizeof(return_path));
304 bzero(orig_pwd, sizeof(orig_pwd));
305 bzero(token_path, sizeof(token_path));
306
307 getcwd(orig_pwd, sizeof(orig_pwd));
308 // Check whether we are under valid path now
309 if ((strcmp(orig_pwd, PATH_SMB_FTPD_ROOT) != 0) &&
310 (smbftpd_is_under_valid_path(validshares, orig_pwd, 0) == 0)) {
311 chdir(PATH_SMB_FTPD_ROOT);
312 return NULL;
313 }
314
315
316 if (tmp_path[0] == '/') {
317 snprintf(token_path, sizeof(token_path), "_@SMB_FTPD_ROOT@_%s", tmp_path);
318 strcpy(return_path, "/");
319 }else{
320 snprintf(token_path, sizeof(token_path), "%s", tmp_path);
321 snprintf(return_path, sizeof(return_path), "%s", tmp_path);
322 }
323
324 // separate the path by '/', and chdir into the path to check
325 // whether each component name is in the valid path
326 for (token = strtok(token_path, "/"); token != NULL; token = strtok(NULL, "/")) {
327 // we are under virtual root
328 if (strcmp(token, "_@SMB_FTPD_ROOT@_") == 0) {
329 snprintf(return_path, sizeof(return_path), "%s", PATH_SMB_FTPD_ROOT);
330 chdir(PATH_SMB_FTPD_ROOT);
331 continue;
332 }
333
334 bzero(curdir, sizeof(curdir));
335 getcwd(curdir, sizeof(curdir));
336
337 // chdir to / when cd .. if we are under the root of share path
338 if ( (strcmp(token, "..") == 0) && (is_share_root(validshares, curdir) == 1) ) {
339 snprintf(return_path, sizeof(return_path), "%s", PATH_SMB_FTPD_ROOT);
340 chdir(PATH_SMB_FTPD_ROOT);
341 continue;
342 }
343
344 // When under virtual root
345 if (strcmp(curdir, PATH_SMB_FTPD_ROOT) == 0) {
346 char *ptr;
347
348 // chdir to virtual root when cd .. or .
349 if ((strcmp(token, ".") == 0) || (strcmp(token, "..") == 0)) {
350 snprintf(return_path, sizeof(return_path), "%s", PATH_SMB_FTPD_ROOT);
351 chdir(PATH_SMB_FTPD_ROOT);
352 continue;
353 // if next path is not valid share path, return NULL
354 } else if ( (ptr = smbftpd_share_path_get(validshares, token)) == NULL) {
355 chdir(orig_pwd);
356 return NULL;
357 } else {
358 snprintf(return_path, sizeof(return_path), "%s", ptr);
359 chdir(ptr);
360 continue;
361 }
362 } else {
363 // We are under valid share path now.
364 if(smbftpd_is_under_valid_path(validshares, curdir, 0) == 0){
365 // Not under valid share path
366 chdir(orig_pwd);
367 return NULL;
368 }
369
370 err = lstat(token, (struct stat *) &statbuf);
371 if (err != 0) {
372 goto LAST_COMPOENT;
373 }
374
375 // Dealing with directory
376 if (S_ISDIR(statbuf.st_mode)){
377 if (chdir(token)) {
378 // Failed to chdir (directory not exist or permission denied)
379 chdir(orig_pwd);
380 return NULL;
381 } else {
382 bzero(curdir, sizeof(curdir));
383 getcwd(curdir, sizeof(curdir));
384 if (smbftpd_is_under_valid_path(validshares, curdir, 0) == 1) {
385 strncpy(return_path, curdir, sizeof(return_path)-1);
386 continue;
387 } else {
388 chdir(orig_pwd);
389 return NULL;
390 }
391 }
392 } else if ( S_ISLNK(statbuf.st_mode) &&
393 !(flags & FLAG_NO_FOLLOW_LINK) ) { // Dealing with link
394
395 if (flags & FLAG_NO_FOLLOW_LAST_LINK) {
396 if (*(token+strlen(token)+1) == '\0') { // Last component
397 goto LAST_COMPOENT;
398 }
399 }
400 err = readlink(token, tmp_path, sizeof(tmp_path) - 1);
401 if (err < 0) {
402 syslog(LOG_ERR, "%s (%d) Why I can lstat, but can't readlink? errno:%d(%s)",
403 __FILE__, __LINE__, errno, strerror(errno));
404 return NULL;
405 } else {
406 tmp_path[err] = '\0';
407 }
408
409 if (*tmp_path != '/'){
410 smbfptd_replace_share_path(validshares, curdir, sizeof(curdir));
411 strcpy(return_path, curdir);
412 snprintf(curdir, sizeof(curdir), "%s/%s", return_path, tmp_path);
413
414 // Re-compose the path and parse again.
415 if ( *(token + strlen(token) + 1) == '\0') {
416 snprintf(tmp_path, sizeof(tmp_path), "%s", curdir);
417 } else {
418 snprintf(tmp_path, sizeof(tmp_path), "%s/%s", curdir, token+strlen(token)+1);
419 }
420 } else {
421 // Re-compose the path and parse again.
422 if ( *(token + strlen(token) + 1) != '\0') {
423 snprintf(tmp_path, sizeof(tmp_path), "%s/%s", curdir, token+strlen(token)+1);
424 }
425 }
426
427 chdir(orig_pwd);
428 goto REDO;
429
430 } else { // Dealing with file
431 LAST_COMPOENT:
432 if ((flags & FLAG_CHECK_WRITABLE) &&
433 smbftpd_is_under_valid_path(validshares, curdir, 1) == 0) {
434 chdir(orig_pwd);
435 return NULL;
436 } else {
437 snprintf(return_path, sizeof(return_path), "%s/%s",
438 curdir, token);
439
440 // There should be no next dir since it's a file
441 if (strtok(NULL, "/")) {
442 chdir(orig_pwd);
443 return NULL;
444 }
445 chdir(orig_pwd);
446 return return_path;
447 }
448 } // end of file or directory
449 } // end of if under virtual root
450 } // end of for
451 if (flags & FLAG_CHECK_WRITABLE) {
452 getcwd(curdir, sizeof(curdir));
453 if (is_share_root(validshares, curdir) ||
454 (smbftpd_is_under_valid_path(validshares, curdir, 1) == 0)) {
455 chdir(orig_pwd);
456 return NULL;
457 }else{
458 chdir(orig_pwd);
459 return return_path;
460 }
461 } else {
462 chdir(orig_pwd);
463 return return_path;
464 }
465 }
466
467 /**
468 * Free the smbftpd_valid_share_t linked list.
469 *
470 * @param validshares
471 */
smbftpd_valid_share_free(smbftpd_valid_share_t ** validshares)472 void smbftpd_valid_share_free(smbftpd_valid_share_t **validshares)
473 {
474 smbftpd_valid_share_t *p;
475
476 while (*validshares) {
477 p = *validshares;
478 *validshares = p->next;
479 if (p->share) {
480 free(p->share);
481 }
482 if (p->path) {
483 free(p->path);
484 }
485 free(p);
486 }
487
488 return;
489 }
490
491 /* Get shares that is accessable by szUser and put in smbftpd_share_t
492 *
493 * Return Values:
494 * 0: Success
495 * -1: Failed
496 */
smbftpd_valid_share_get(const char * user,const char * home_dir,smbftpd_share_t * shares,smbftpd_valid_share_t ** ppvalid_shares)497 int smbftpd_valid_share_get(const char *user, const char *home_dir,
498 smbftpd_share_t *shares, smbftpd_valid_share_t **ppvalid_shares)
499 {
500 smbftpd_share_t *curr;
501 struct stat statcheck;
502 char *home = NULL;
503 int writable = 0;
504 int err = -1;
505 int deny;
506
507 if (NULL == user) {
508 syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
509 return -1;
510 }
511
512 if (NULL == shares) {
513 *ppvalid_shares = NULL;
514 return 0;
515 }
516
517 curr = shares;
518 while (curr) {
519 home = NULL;
520 deny = 1;
521 writable = 0;
522
523 if (strcmp(curr->share, "homes") == 0) {
524 char *ptr = real_full_path(home_dir);
525 if (!ptr) {
526 goto SKIP;
527 }
528 home = strdup(ptr);
529 if (!home) {
530 syslog(LOG_ERR, "%s (%d) Failed to strdup(%s). (%s)",
531 __FILE__, __LINE__, smbftpd_session.home, strerror(errno));
532 goto Error;
533 }
534 deny = 0;
535 writable = 1;
536 goto SKIP;
537 }
538
539 if (1 == is_user_in_list(user, curr->rw)) {
540 writable = 1;
541 deny = 0;
542 }
543
544 if (deny == 1) {
545 if (1 == is_user_in_list(user, curr->ro)) {
546 deny = 0;
547 }
548 }
549 SKIP:
550 // Shared is accessable for szUser
551 if (deny == 0) {
552 if (home || ((!stat(curr->path, &statcheck)) &&
553 (S_ISDIR(statcheck.st_mode)))) {
554 smbftpd_valid_share_t *pSet;
555
556 pSet = calloc(1, sizeof(smbftpd_valid_share_t));
557 if (pSet == NULL) {
558 syslog(LOG_ERR, "%s (%d) Ran out of memory.", __FILE__, __LINE__);
559 if (home) {
560 free(home);
561 }
562 goto Error;
563 }
564 pSet->share = home?strdup("home"):strdup(curr->share);
565 pSet->path = home?home:strdup(curr->path);
566 pSet->browseable = curr->browseable;
567
568 if (writable) {
569 pSet->writable = 1;
570 }
571 if (is_user_in_list(user, curr->disable_download)) {
572 pSet->disable_download = 1;
573 }
574 if (is_user_in_list(user, curr->disable_ls)) {
575 pSet->disable_ls = 1;
576 }
577 if (is_user_in_list(user, curr->disable_modify)) {
578 pSet->disable_modify = 1;
579 }
580 pSet->next = *ppvalid_shares;
581 *ppvalid_shares = pSet;
582
583 }
584 }
585 curr = curr->next;
586 }
587
588 err = 0;
589 Error:
590 if (err) {
591 smbftpd_valid_share_free(ppvalid_shares);
592 }
593 return err;
594 }
595
596 /**
597 * Free all smbftpd_share linked list.
598 *
599 * Make sure you set the smb_shares->next to NULL if you need to
600 * free only one share.
601 *
602 * @param smb_shares The linked list of smbftpd_share_t.
603 */
smbftpd_share_free(smbftpd_share_t ** smb_shares)604 void smbftpd_share_free(smbftpd_share_t **smb_shares)
605 {
606 smbftpd_share_t *p;
607
608 while (*smb_shares) {
609 p = *smb_shares;
610 *smb_shares = p->next;
611
612 if (p->share) {
613 free(p->share);
614 }
615 if (p->path) {
616 free(p->path);
617 }
618 if (p->rw) {
619 free(p->rw);
620 }
621 if (p->ro) {
622 free(p->ro);
623 }
624 if (p->disable_download) {
625 free(p->disable_download);
626 }
627 if (p->disable_ls) {
628 free(p->disable_ls);
629 }
630 free(p);
631 }
632 }
633
634 /**
635 * This function will read the configuration in given smbftpd_share.conf
636 *
637 * It will allocate memory for smb_shares to store share
638 * information. So smbftpd_share_free() must be call to free it.
639 *
640 * @param path
641 * @param smb_shares
642 *
643 * @return 0: Success
644 * -1: Failed
645 */
smbftpd_share_enum(char * path,smbftpd_share_t ** smb_shares)646 int smbftpd_share_enum(char *path, smbftpd_share_t **smb_shares)
647 {
648 smbftpd_share_t *prev, *cur;
649 FILE *fp = NULL;
650 char line[LINE_MAX], *ptr1, *ptr2;
651 int err = -1;
652
653 if (NULL == path) {
654 syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
655 return -1;
656 }
657
658 fp = fopen(path, "r");
659 if (NULL == fp) {
660 syslog(LOG_ERR, "%s (%d) Failed to open share config [%s], errno:%d (%s)",
661 __FILE__, __LINE__, path, errno, strerror(errno));
662 goto Error;
663 }
664
665 while (NULL != fgets(line, sizeof(line), fp)) {
666 // Skip spaces
667 ptr1 = str_trim_space(line);
668
669 if ((*ptr1 == '#') || (*ptr1 == '\0') || (*ptr1 == '\n')){
670 continue;
671 } else if (*ptr1 == '[') {
672 ptr1++;
673 ptr2 = strchr(ptr1, ']');
674 if ((ptr2 != NULL) && (ptr2 - ptr1 > 0)) {
675 smbftpd_share_t *p;
676
677 *ptr2 = '\0';
678 str_trim_space(ptr1);
679
680 p = calloc(1, sizeof(smbftpd_share_t));
681 if (p == NULL) {
682 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
683 goto Error;
684 }
685 p->share = strdup(ptr1);
686 if (!p->share) {
687 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
688 free(p);
689 goto Error;
690 }
691 p->browseable = 1;
692 p->next = *smb_shares;
693 *smb_shares = p;
694 }
695 } else {
696 if (!*smb_shares) {
697 /* We have not gotten a section */
698 continue;
699 }
700 ptr2 = strchr(ptr1, '=');
701 if (NULL == ptr2) {
702 continue;
703 }
704 *ptr2 = '\0';
705 ptr2++;
706 str_trim_space_quote(ptr1);
707 str_trim_space_quote(ptr2);
708 if (strcmp(ptr1, "rw") == 0) {
709 (*smb_shares)->rw = strdup(ptr2);
710 if (!(*smb_shares)->rw) {
711 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
712 goto Error;
713 }
714 } else if (strcmp(ptr1, "ro") == 0) {
715 (*smb_shares)->ro = strdup(ptr2);
716 if (!(*smb_shares)->ro) {
717 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
718 goto Error;
719 }
720 } else if (strcmp(ptr1, "disable_download") == 0) {
721 (*smb_shares)->disable_download = strdup(ptr2);
722 if (!(*smb_shares)->disable_download) {
723 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
724 goto Error;
725 }
726 } else if (strcmp(ptr1, "disable_ls") == 0) {
727 (*smb_shares)->disable_ls = strdup(ptr2);
728 if (!(*smb_shares)->disable_ls) {
729 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
730 goto Error;
731 }
732 } else if (strcmp(ptr1, "disable_modify") == 0) {
733 (*smb_shares)->disable_modify = strdup(ptr2);
734 if (!(*smb_shares)->disable_modify) {
735 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
736 goto Error;
737 }
738 } else if (strcmp(ptr1, "path") == 0) {
739
740 char *ptr = real_full_path(ptr2);
741 if (!ptr) {
742 syslog(LOG_ERR, "%s (%d) path \"%s\" does not exist.",
743 __FILE__, __LINE__, ptr2);
744 } else {
745 (*smb_shares)->path = strdup(ptr);
746 if (!(*smb_shares)->path) {
747 syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
748 goto Error;
749 }
750 }
751 } else if (strcmp(ptr1, "browseable") == 0) {
752 if (strcasecmp(ptr2, "no") == 0) {
753 (*smb_shares)->browseable = 0;
754 }
755 }
756 }
757 }
758
759 // Remove shares that have no path beside homes
760 prev = cur = *smb_shares;
761 while (cur) {
762 if ((strcmp(cur->share, "homes") != 0) && !cur->path) {
763 if (prev == cur) { /* Head */
764 *smb_shares = cur->next;
765 cur->next = NULL;
766 smbftpd_share_free(&cur);
767 prev = cur = *smb_shares;
768 } else {
769 prev->next = cur->next;
770 cur->next = NULL;
771 smbftpd_share_free(&cur);
772 cur = prev->next;
773 }
774 } else {
775 prev = cur;
776 cur = cur->next;
777 }
778 }
779
780 err = 0;
781 Error:
782 if (fp) {
783 fclose(fp);
784 }
785 if (err) {
786 if (*smb_shares != NULL) {
787 smbftpd_share_free(smb_shares);
788 }
789 return -1;
790 } else {
791 return 0;
792 }
793 }
794
795