Line data Source code
1 : /*
2 : Unix SMB/Netbios implementation.
3 : SMB client library implementation
4 : Copyright (C) Andrew Tridgell 1998
5 : Copyright (C) Richard Sharpe 2000, 2002
6 : Copyright (C) John Terpstra 2000
7 : Copyright (C) Tom Jansen (Ninja ISD) 2002
8 : Copyright (C) Derrell Lipman 2003-2008
9 : Copyright (C) Jeremy Allison 2007, 2008
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "includes.h"
26 : #include "libsmb/namequery.h"
27 : #include "libsmb/libsmb.h"
28 : #include "libsmbclient.h"
29 : #include "libsmb_internal.h"
30 : #include "rpc_client/cli_pipe.h"
31 : #include "../librpc/gen_ndr/ndr_srvsvc_c.h"
32 : #include "libsmb/nmblib.h"
33 : #include "../libcli/smb/smbXcli_base.h"
34 : #include "../libcli/security/security.h"
35 : #include "lib/util/tevent_ntstatus.h"
36 : #include "lib/util/time_basic.h"
37 : #include "lib/util/string_wrappers.h"
38 :
39 : /*
40 : * Routine to open a directory
41 : * We accept the URL syntax explained in SMBC_parse_path(), above.
42 : */
43 :
44 52 : static void remove_dirplus(SMBCFILE *dir)
45 : {
46 52 : struct smbc_dirplus_list *d = NULL;
47 :
48 52 : d = dir->dirplus_list;
49 1186 : while (d != NULL) {
50 1082 : struct smbc_dirplus_list *f = d;
51 1082 : d = d->next;
52 :
53 1082 : SAFE_FREE(f->smb_finfo->short_name);
54 1082 : SAFE_FREE(f->smb_finfo->name);
55 1082 : SAFE_FREE(f->smb_finfo);
56 1082 : SAFE_FREE(f);
57 : }
58 :
59 52 : dir->dirplus_list = NULL;
60 52 : dir->dirplus_end = NULL;
61 52 : dir->dirplus_next = NULL;
62 52 : }
63 :
64 : static void
65 52 : remove_dir(SMBCFILE *dir)
66 : {
67 : struct smbc_dir_list *d,*f;
68 :
69 52 : d = dir->dir_list;
70 1766 : while (d) {
71 :
72 1662 : f = d; d = d->next;
73 :
74 1662 : SAFE_FREE(f->dirent);
75 1662 : SAFE_FREE(f);
76 :
77 : }
78 :
79 52 : dir->dir_list = dir->dir_end = dir->dir_next = NULL;
80 :
81 52 : }
82 :
83 : static int
84 1662 : add_dirent(SMBCFILE *dir,
85 : const char *name,
86 : const char *comment,
87 : uint32_t type)
88 : {
89 : struct smbc_dirent *dirent;
90 : int size;
91 1662 : int name_length = (name == NULL ? 0 : strlen(name));
92 1662 : int comment_len = (comment == NULL ? 0 : strlen(comment));
93 :
94 : /*
95 : * Allocate space for the dirent, which must be increased by the
96 : * size of the name and the comment and 1 each for the null terminator.
97 : */
98 :
99 1662 : size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
100 :
101 1662 : dirent = (struct smbc_dirent *)SMB_MALLOC(size);
102 :
103 1662 : if (!dirent) {
104 :
105 0 : dir->dir_error = ENOMEM;
106 0 : return -1;
107 :
108 : }
109 :
110 1662 : ZERO_STRUCTP(dirent);
111 :
112 1662 : if (dir->dir_list == NULL) {
113 :
114 50 : dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
115 50 : if (!dir->dir_list) {
116 :
117 0 : SAFE_FREE(dirent);
118 0 : dir->dir_error = ENOMEM;
119 0 : return -1;
120 :
121 : }
122 50 : ZERO_STRUCTP(dir->dir_list);
123 :
124 50 : dir->dir_end = dir->dir_next = dir->dir_list;
125 : }
126 : else {
127 :
128 1612 : dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
129 :
130 1612 : if (!dir->dir_end->next) {
131 :
132 0 : SAFE_FREE(dirent);
133 0 : dir->dir_error = ENOMEM;
134 0 : return -1;
135 :
136 : }
137 1612 : ZERO_STRUCTP(dir->dir_end->next);
138 :
139 1612 : dir->dir_end = dir->dir_end->next;
140 : }
141 :
142 1662 : dir->dir_end->next = NULL;
143 1662 : dir->dir_end->dirent = dirent;
144 :
145 1662 : dirent->smbc_type = type;
146 1662 : dirent->namelen = name_length;
147 1662 : dirent->commentlen = comment_len;
148 1662 : dirent->dirlen = size;
149 :
150 : /*
151 : * dirent->namelen + 1 includes the null (no null termination needed)
152 : * Ditto for dirent->commentlen.
153 : * The space for the two null bytes was allocated.
154 : */
155 1662 : strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
156 1662 : dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
157 1662 : strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
158 :
159 1662 : return 0;
160 :
161 : }
162 :
163 1082 : static int add_dirplus(SMBCFILE *dir, struct file_info *finfo)
164 : {
165 1082 : struct smbc_dirplus_list *new_entry = NULL;
166 1082 : struct libsmb_file_info *info = NULL;
167 :
168 1082 : new_entry = SMB_MALLOC_P(struct smbc_dirplus_list);
169 1082 : if (new_entry == NULL) {
170 0 : dir->dir_error = ENOMEM;
171 0 : return -1;
172 : }
173 1082 : ZERO_STRUCTP(new_entry);
174 1082 : new_entry->ino = finfo->ino;
175 :
176 1082 : info = SMB_MALLOC_P(struct libsmb_file_info);
177 1082 : if (info == NULL) {
178 0 : SAFE_FREE(new_entry);
179 0 : dir->dir_error = ENOMEM;
180 0 : return -1;
181 : }
182 :
183 1082 : ZERO_STRUCTP(info);
184 :
185 1082 : info->btime_ts = finfo->btime_ts;
186 1082 : info->atime_ts = finfo->atime_ts;
187 1082 : info->ctime_ts = finfo->ctime_ts;
188 1082 : info->mtime_ts = finfo->mtime_ts;
189 1082 : info->gid = finfo->gid;
190 1082 : info->attrs = finfo->attr;
191 1082 : info->size = finfo->size;
192 1082 : info->uid = finfo->uid;
193 1082 : info->name = SMB_STRDUP(finfo->name);
194 1082 : if (info->name == NULL) {
195 0 : SAFE_FREE(info);
196 0 : SAFE_FREE(new_entry);
197 0 : dir->dir_error = ENOMEM;
198 0 : return -1;
199 : }
200 :
201 1082 : if (finfo->short_name) {
202 772 : info->short_name = SMB_STRDUP(finfo->short_name);
203 : } else {
204 310 : info->short_name = SMB_STRDUP("");
205 : }
206 :
207 1082 : if (info->short_name == NULL) {
208 0 : SAFE_FREE(info->name);
209 0 : SAFE_FREE(info);
210 0 : SAFE_FREE(new_entry);
211 0 : dir->dir_error = ENOMEM;
212 0 : return -1;
213 : }
214 1082 : new_entry->smb_finfo = info;
215 :
216 : /* Now add to the list. */
217 1082 : if (dir->dirplus_list == NULL) {
218 : /* Empty list - point everything at new_entry. */
219 42 : dir->dirplus_list = new_entry;
220 42 : dir->dirplus_end = new_entry;
221 42 : dir->dirplus_next = new_entry;
222 : } else {
223 : /* Append to list but leave the ->next cursor alone. */
224 1040 : dir->dirplus_end->next = new_entry;
225 1040 : dir->dirplus_end = new_entry;
226 : }
227 :
228 1082 : return 0;
229 : }
230 :
231 : static void
232 6 : list_unique_wg_fn(const char *name,
233 : uint32_t type,
234 : const char *comment,
235 : void *state)
236 : {
237 6 : SMBCFILE *dir = (SMBCFILE *)state;
238 : struct smbc_dir_list *dir_list;
239 : struct smbc_dirent *dirent;
240 : int dirent_type;
241 6 : int do_remove = 0;
242 :
243 6 : dirent_type = dir->dir_type;
244 :
245 6 : if (add_dirent(dir, name, comment, dirent_type) < 0) {
246 : /* An error occurred, what do we do? */
247 : /* FIXME: Add some code here */
248 : /* Change cli_NetServerEnum to take a fn
249 : returning NTSTATUS... JRA. */
250 : }
251 :
252 : /* Point to the one just added */
253 6 : dirent = dir->dir_end->dirent;
254 :
255 : /* See if this was a duplicate */
256 18 : for (dir_list = dir->dir_list;
257 12 : dir_list != dir->dir_end;
258 6 : dir_list = dir_list->next) {
259 12 : if (! do_remove &&
260 6 : strcmp(dir_list->dirent->name, dirent->name) == 0) {
261 : /* Duplicate. End end of list need to be removed. */
262 0 : do_remove = 1;
263 : }
264 :
265 6 : if (do_remove && dir_list->next == dir->dir_end) {
266 : /* Found the end of the list. Remove it. */
267 0 : dir->dir_end = dir_list;
268 0 : free(dir_list->next);
269 0 : free(dirent);
270 0 : dir_list->next = NULL;
271 0 : break;
272 : }
273 : }
274 6 : }
275 :
276 : static void
277 574 : list_fn(const char *name,
278 : uint32_t type,
279 : const char *comment,
280 : void *state)
281 : {
282 574 : SMBCFILE *dir = (SMBCFILE *)state;
283 : int dirent_type;
284 :
285 : /*
286 : * We need to process the type a little ...
287 : *
288 : * Disk share = 0x00000000
289 : * Print share = 0x00000001
290 : * Comms share = 0x00000002 (obsolete?)
291 : * IPC$ share = 0x00000003
292 : *
293 : * administrative shares:
294 : * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
295 : */
296 :
297 574 : if (dir->dir_type == SMBC_FILE_SHARE) {
298 568 : switch (type) {
299 544 : case 0 | 0x80000000:
300 : case 0:
301 544 : dirent_type = SMBC_FILE_SHARE;
302 544 : break;
303 :
304 20 : case 1:
305 20 : dirent_type = SMBC_PRINTER_SHARE;
306 20 : break;
307 :
308 0 : case 2:
309 0 : dirent_type = SMBC_COMMS_SHARE;
310 0 : break;
311 :
312 4 : case 3 | 0x80000000:
313 : case 3:
314 4 : dirent_type = SMBC_IPC_SHARE;
315 4 : break;
316 :
317 0 : default:
318 0 : dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
319 0 : break;
320 : }
321 : }
322 : else {
323 6 : dirent_type = dir->dir_type;
324 : }
325 :
326 574 : if (add_dirent(dir, name, comment, dirent_type) < 0) {
327 : /* An error occurred, what do we do? */
328 : /* FIXME: Add some code here */
329 : /* Change cli_NetServerEnum to take a fn
330 : returning NTSTATUS... JRA. */
331 : }
332 574 : }
333 :
334 : static NTSTATUS
335 1082 : dir_list_fn(struct file_info *finfo,
336 : const char *mask,
337 : void *state)
338 : {
339 1082 : SMBCFILE *dirp = (SMBCFILE *)state;
340 : int ret;
341 :
342 1082 : if (add_dirent((SMBCFILE *)state, finfo->name, "",
343 1082 : (finfo->attr&FILE_ATTRIBUTE_DIRECTORY?SMBC_DIR:SMBC_FILE)) < 0) {
344 0 : SMBCFILE *dir = (SMBCFILE *)state;
345 0 : return map_nt_error_from_unix(dir->dir_error);
346 : }
347 1082 : ret = add_dirplus(dirp, finfo);
348 1082 : if (ret < 0) {
349 0 : return map_nt_error_from_unix(dirp->dir_error);
350 : }
351 1082 : return NT_STATUS_OK;
352 : }
353 :
354 : static NTSTATUS
355 4 : net_share_enum_rpc(struct cli_state *cli,
356 : void (*fn)(const char *name,
357 : uint32_t type,
358 : const char *comment,
359 : void *state),
360 : void *state)
361 : {
362 : uint32_t i;
363 : WERROR result;
364 4 : uint32_t preferred_len = 0xffffffff;
365 : uint32_t type;
366 : struct srvsvc_NetShareInfoCtr info_ctr;
367 : struct srvsvc_NetShareCtr1 ctr1;
368 4 : fstring name = "";
369 4 : fstring comment = "";
370 4 : struct rpc_pipe_client *pipe_hnd = NULL;
371 : NTSTATUS nt_status;
372 4 : uint32_t resume_handle = 0;
373 4 : uint32_t total_entries = 0;
374 : struct dcerpc_binding_handle *b;
375 :
376 : /* Open the server service pipe */
377 4 : nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc,
378 : &pipe_hnd);
379 4 : if (!NT_STATUS_IS_OK(nt_status)) {
380 0 : DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
381 0 : goto done;
382 : }
383 :
384 4 : ZERO_STRUCT(info_ctr);
385 4 : ZERO_STRUCT(ctr1);
386 :
387 4 : info_ctr.level = 1;
388 4 : info_ctr.ctr.ctr1 = &ctr1;
389 :
390 4 : b = pipe_hnd->binding_handle;
391 :
392 : /* Issue the NetShareEnum RPC call and retrieve the response */
393 4 : nt_status = dcerpc_srvsvc_NetShareEnumAll(b, talloc_tos(),
394 4 : pipe_hnd->desthost,
395 : &info_ctr,
396 : preferred_len,
397 : &total_entries,
398 : &resume_handle,
399 : &result);
400 :
401 : /* Was it successful? */
402 4 : if (!NT_STATUS_IS_OK(nt_status)) {
403 : /* Nope. Go clean up. */
404 0 : goto done;
405 : }
406 :
407 4 : if (!W_ERROR_IS_OK(result)) {
408 : /* Nope. Go clean up. */
409 0 : nt_status = werror_to_ntstatus(result);
410 0 : goto done;
411 : }
412 :
413 4 : if (total_entries == 0) {
414 : /* Nope. Go clean up. */
415 0 : nt_status = NT_STATUS_NOT_FOUND;
416 0 : goto done;
417 : }
418 :
419 : /* For each returned entry... */
420 572 : for (i = 0; i < info_ctr.ctr.ctr1->count; i++) {
421 :
422 : /* pull out the share name */
423 568 : fstrcpy(name, info_ctr.ctr.ctr1->array[i].name);
424 :
425 : /* pull out the share's comment */
426 568 : fstrcpy(comment, info_ctr.ctr.ctr1->array[i].comment);
427 :
428 : /* Get the type value */
429 568 : type = info_ctr.ctr.ctr1->array[i].type;
430 :
431 : /* Add this share to the list */
432 568 : (*fn)(name, type, comment, state);
433 : }
434 :
435 4 : done:
436 : /* Close the server service pipe */
437 4 : TALLOC_FREE(pipe_hnd);
438 :
439 : /* Tell 'em if it worked */
440 4 : return nt_status;
441 : }
442 :
443 :
444 : /*
445 : * Verify that the options specified in a URL are valid
446 : */
447 : int
448 62 : SMBC_check_options(char *server,
449 : char *share,
450 : char *path,
451 : char *options)
452 : {
453 62 : DEBUG(4, ("SMBC_check_options(): server='%s' share='%s' "
454 : "path='%s' options='%s'\n",
455 : server, share, path, options));
456 :
457 : /* No options at all is always ok */
458 62 : if (! *options) return 0;
459 :
460 : /* Currently, we don't support any options. */
461 0 : return -1;
462 : }
463 :
464 :
465 : SMBCFILE *
466 114 : SMBC_opendir_ctx(SMBCCTX *context,
467 : const char *fname)
468 : {
469 114 : char *server = NULL;
470 114 : char *share = NULL;
471 114 : char *user = NULL;
472 114 : char *password = NULL;
473 114 : char *options = NULL;
474 114 : char *workgroup = NULL;
475 114 : char *path = NULL;
476 114 : size_t path_len = 0;
477 114 : uint16_t port = 0;
478 114 : SMBCSRV *srv = NULL;
479 114 : SMBCFILE *dir = NULL;
480 : struct sockaddr_storage rem_ss;
481 114 : TALLOC_CTX *frame = talloc_stackframe();
482 :
483 114 : if (!context || !context->internal->initialized) {
484 0 : DEBUG(4, ("no valid context\n"));
485 0 : TALLOC_FREE(frame);
486 0 : errno = EINVAL + 8192;
487 0 : return NULL;
488 :
489 : }
490 :
491 114 : if (!fname) {
492 4 : DEBUG(4, ("no valid fname\n"));
493 4 : TALLOC_FREE(frame);
494 4 : errno = EINVAL + 8193;
495 4 : return NULL;
496 : }
497 :
498 110 : if (SMBC_parse_path(frame,
499 : context,
500 : fname,
501 : &workgroup,
502 : &server,
503 : &port,
504 : &share,
505 : &path,
506 : &user,
507 : &password,
508 : &options)) {
509 48 : DEBUG(4, ("no valid path\n"));
510 48 : TALLOC_FREE(frame);
511 48 : errno = EINVAL + 8194;
512 48 : return NULL;
513 : }
514 :
515 62 : DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
516 : "path='%s' options='%s'\n",
517 : fname, server, share, path, options));
518 :
519 : /* Ensure the options are valid */
520 62 : if (SMBC_check_options(server, share, path, options)) {
521 0 : DEBUG(4, ("unacceptable options (%s)\n", options));
522 0 : TALLOC_FREE(frame);
523 0 : errno = EINVAL + 8195;
524 0 : return NULL;
525 : }
526 :
527 62 : if (!user || user[0] == (char)0) {
528 46 : user = talloc_strdup(frame, smbc_getUser(context));
529 46 : if (!user) {
530 0 : TALLOC_FREE(frame);
531 0 : errno = ENOMEM;
532 0 : return NULL;
533 : }
534 : }
535 :
536 62 : dir = SMB_MALLOC_P(SMBCFILE);
537 :
538 62 : if (!dir) {
539 0 : TALLOC_FREE(frame);
540 0 : errno = ENOMEM;
541 0 : return NULL;
542 : }
543 :
544 62 : ZERO_STRUCTP(dir);
545 :
546 62 : dir->cli_fd = 0;
547 62 : dir->fname = SMB_STRDUP(fname);
548 62 : if (dir->fname == NULL) {
549 0 : SAFE_FREE(dir);
550 0 : TALLOC_FREE(frame);
551 0 : errno = ENOMEM;
552 0 : return NULL;
553 : }
554 62 : dir->srv = NULL;
555 62 : dir->offset = 0;
556 62 : dir->file = False;
557 62 : dir->dir_list = dir->dir_next = dir->dir_end = NULL;
558 :
559 62 : if (server[0] == (char)0) {
560 :
561 : size_t i;
562 4 : size_t count = 0;
563 : size_t max_lmb_count;
564 : struct sockaddr_storage *ip_list;
565 : struct sockaddr_storage server_addr;
566 4 : struct cli_credentials *creds = NULL;
567 : NTSTATUS status;
568 :
569 4 : if (share[0] != (char)0 || path[0] != (char)0) {
570 :
571 0 : if (dir) {
572 0 : SAFE_FREE(dir->fname);
573 0 : SAFE_FREE(dir);
574 : }
575 0 : TALLOC_FREE(frame);
576 0 : errno = EINVAL + 8196;
577 0 : return NULL;
578 : }
579 :
580 : /* Determine how many local master browsers to query */
581 8 : max_lmb_count = (smbc_getOptionBrowseMaxLmbCount(context) == 0
582 : ? INT_MAX
583 4 : : smbc_getOptionBrowseMaxLmbCount(context));
584 :
585 4 : creds = cli_credentials_init(frame);
586 4 : if (creds == NULL) {
587 0 : if (dir) {
588 0 : SAFE_FREE(dir->fname);
589 0 : SAFE_FREE(dir);
590 : }
591 0 : TALLOC_FREE(frame);
592 0 : errno = ENOMEM;
593 0 : return NULL;
594 : }
595 :
596 4 : (void)cli_credentials_set_username(creds, user, CRED_SPECIFIED);
597 4 : (void)cli_credentials_set_password(creds, password, CRED_SPECIFIED);
598 :
599 : /*
600 : * We have server and share and path empty but options
601 : * requesting that we scan all master browsers for their list
602 : * of workgroups/domains. This implies that we must first try
603 : * broadcast queries to find all master browsers, and if that
604 : * doesn't work, then try our other methods which return only
605 : * a single master browser.
606 : */
607 :
608 4 : ip_list = NULL;
609 4 : status = name_resolve_bcast(talloc_tos(),
610 : MSBROWSE,
611 : 1,
612 : &ip_list,
613 : &count);
614 4 : if (!NT_STATUS_IS_OK(status))
615 : {
616 :
617 0 : TALLOC_FREE(ip_list);
618 :
619 0 : if (!find_master_ip(workgroup, &server_addr)) {
620 :
621 0 : if (dir) {
622 0 : SAFE_FREE(dir->fname);
623 0 : SAFE_FREE(dir);
624 : }
625 0 : TALLOC_FREE(frame);
626 0 : errno = ENOENT;
627 0 : return NULL;
628 : }
629 :
630 0 : ip_list = (struct sockaddr_storage *)talloc_memdup(
631 : talloc_tos(), &server_addr,
632 : sizeof(server_addr));
633 0 : if (ip_list == NULL) {
634 0 : if (dir) {
635 0 : SAFE_FREE(dir->fname);
636 0 : SAFE_FREE(dir);
637 : }
638 0 : TALLOC_FREE(frame);
639 0 : errno = ENOMEM;
640 0 : return NULL;
641 : }
642 0 : count = 1;
643 : }
644 :
645 16 : for (i = 0; i < count && i < max_lmb_count; i++) {
646 : char addr[INET6_ADDRSTRLEN];
647 12 : char *wg_ptr = NULL;
648 12 : struct cli_state *cli = NULL;
649 :
650 12 : print_sockaddr(addr, sizeof(addr), &ip_list[i]);
651 12 : DEBUG(99, ("Found master browser %zu of %zu: %s\n",
652 : i+1, MAX(count, max_lmb_count),
653 : addr));
654 :
655 24 : cli = get_ipc_connect_master_ip(talloc_tos(),
656 12 : &ip_list[i],
657 : creds,
658 : &wg_ptr);
659 : /* cli == NULL is the master browser refused to talk or
660 : could not be found */
661 12 : if (!cli) {
662 18 : continue;
663 : }
664 :
665 4 : workgroup = talloc_strdup(frame, wg_ptr);
666 4 : server = talloc_strdup(frame, smbXcli_conn_remote_name(cli->conn));
667 :
668 4 : cli_shutdown(cli);
669 :
670 4 : if (!workgroup || !server) {
671 0 : if (dir) {
672 0 : SAFE_FREE(dir->fname);
673 0 : SAFE_FREE(dir);
674 : }
675 0 : TALLOC_FREE(frame);
676 0 : errno = ENOMEM;
677 0 : return NULL;
678 : }
679 :
680 4 : DEBUG(4, ("using workgroup %s %s\n",
681 : workgroup, server));
682 :
683 : /*
684 : * For each returned master browser IP address, get a
685 : * connection to IPC$ on the server if we do not
686 : * already have one, and determine the
687 : * workgroups/domains that it knows about.
688 : */
689 :
690 4 : srv = SMBC_server(frame, context, True, server, port, "IPC$",
691 : &workgroup, &user, &password);
692 4 : if (!srv) {
693 0 : continue;
694 : }
695 :
696 4 : if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) {
697 2 : continue;
698 : }
699 :
700 2 : dir->srv = srv;
701 2 : dir->dir_type = SMBC_WORKGROUP;
702 :
703 : /* Now, list the stuff ... */
704 :
705 2 : if (!cli_NetServerEnum(srv->cli,
706 : workgroup,
707 : SV_TYPE_DOMAIN_ENUM,
708 : list_unique_wg_fn,
709 : (void *)dir)) {
710 0 : continue;
711 : }
712 : }
713 :
714 4 : TALLOC_FREE(ip_list);
715 : } else {
716 : /*
717 : * Server not an empty string ... Check the rest and see what
718 : * gives
719 : */
720 58 : if (*share == '\0') {
721 16 : if (*path != '\0') {
722 :
723 : /* Should not have empty share with path */
724 0 : if (dir) {
725 0 : SAFE_FREE(dir->fname);
726 0 : SAFE_FREE(dir);
727 : }
728 0 : TALLOC_FREE(frame);
729 0 : errno = EINVAL + 8197;
730 0 : return NULL;
731 :
732 : }
733 :
734 : /*
735 : * We don't know if <server> is really a server name
736 : * or is a workgroup/domain name. If we already have
737 : * a server structure for it, we'll use it.
738 : * Otherwise, check to see if <server><1D>,
739 : * <server><1B>, or <server><20> translates. We check
740 : * to see if <server> is an IP address first.
741 : */
742 :
743 : /*
744 : * See if we have an existing server. Do not
745 : * establish a connection if one does not already
746 : * exist.
747 : */
748 16 : srv = SMBC_server(frame, context, False,
749 : server, port, "IPC$",
750 : &workgroup, &user, &password);
751 :
752 : /*
753 : * If no existing server and not an IP addr, look for
754 : * LMB or DMB
755 : */
756 32 : if (!srv &&
757 32 : !is_ipaddress(server) &&
758 20 : (resolve_name(server, &rem_ss, 0x1d, false) || /* LMB */
759 6 : resolve_name(server, &rem_ss, 0x1b, false) )) { /* DMB */
760 : /*
761 : * "server" is actually a workgroup name,
762 : * not a server. Make this clear.
763 : */
764 12 : char *wgroup = server;
765 : fstring buserver;
766 :
767 12 : dir->dir_type = SMBC_SERVER;
768 :
769 : /*
770 : * Get the backup list ...
771 : */
772 12 : if (!name_status_find(wgroup, 0, 0,
773 : &rem_ss, buserver)) {
774 : char addr[INET6_ADDRSTRLEN];
775 :
776 0 : print_sockaddr(addr, sizeof(addr), &rem_ss);
777 0 : DEBUG(0,("Could not get name of "
778 : "local/domain master browser "
779 : "for workgroup %s from "
780 : "address %s\n",
781 : wgroup,
782 : addr));
783 0 : if (dir) {
784 0 : SAFE_FREE(dir->fname);
785 0 : SAFE_FREE(dir);
786 : }
787 0 : TALLOC_FREE(frame);
788 0 : errno = EPERM;
789 0 : return NULL;
790 :
791 : }
792 :
793 : /*
794 : * Get a connection to IPC$ on the server if
795 : * we do not already have one
796 : */
797 12 : srv = SMBC_server(frame, context, True,
798 : buserver, port, "IPC$",
799 : &workgroup,
800 : &user, &password);
801 12 : if (!srv) {
802 4 : DEBUG(0, ("got no contact to IPC$\n"));
803 4 : if (dir) {
804 4 : SAFE_FREE(dir->fname);
805 4 : SAFE_FREE(dir);
806 : }
807 4 : TALLOC_FREE(frame);
808 4 : return NULL;
809 :
810 : }
811 :
812 8 : dir->srv = srv;
813 :
814 8 : if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) {
815 6 : if (dir) {
816 6 : SAFE_FREE(dir->fname);
817 6 : SAFE_FREE(dir);
818 : }
819 6 : TALLOC_FREE(frame);
820 6 : return NULL;
821 : }
822 :
823 : /* Now, list the servers ... */
824 2 : if (!cli_NetServerEnum(srv->cli, wgroup,
825 : 0x0000FFFE, list_fn,
826 : (void *)dir)) {
827 :
828 0 : if (dir) {
829 0 : SAFE_FREE(dir->fname);
830 0 : SAFE_FREE(dir);
831 : }
832 0 : TALLOC_FREE(frame);
833 0 : return NULL;
834 : }
835 8 : } else if (srv ||
836 8 : (resolve_name(server, &rem_ss, 0x20, false))) {
837 : NTSTATUS status;
838 :
839 : /*
840 : * If we hadn't found the server, get one now
841 : */
842 4 : if (!srv) {
843 4 : srv = SMBC_server(frame, context, True,
844 : server, port, "IPC$",
845 : &workgroup,
846 : &user, &password);
847 : }
848 :
849 4 : if (!srv) {
850 0 : if (dir) {
851 0 : SAFE_FREE(dir->fname);
852 0 : SAFE_FREE(dir);
853 : }
854 0 : TALLOC_FREE(frame);
855 0 : return NULL;
856 :
857 : }
858 :
859 4 : dir->dir_type = SMBC_FILE_SHARE;
860 4 : dir->srv = srv;
861 :
862 : /* List the shares ... */
863 :
864 4 : status = net_share_enum_rpc(srv->cli,
865 : list_fn,
866 : (void *)dir);
867 4 : if (!NT_STATUS_IS_OK(status) &&
868 0 : smbXcli_conn_protocol(srv->cli->conn) <=
869 : PROTOCOL_NT1) {
870 : /*
871 : * Only call cli_RNetShareEnum()
872 : * on SMB1 connections, not SMB2+.
873 : */
874 0 : int rc = cli_RNetShareEnum(srv->cli,
875 : list_fn,
876 : (void *)dir);
877 0 : if (rc != 0) {
878 0 : status = cli_nt_error(srv->cli);
879 : } else {
880 0 : status = NT_STATUS_OK;
881 : }
882 : }
883 4 : if (!NT_STATUS_IS_OK(status)) {
884 : /*
885 : * Set cli->raw_status so SMBC_errno()
886 : * will correctly return the error.
887 : */
888 0 : srv->cli->raw_status = status;
889 0 : if (dir != NULL) {
890 0 : SAFE_FREE(dir->fname);
891 0 : SAFE_FREE(dir);
892 : }
893 0 : TALLOC_FREE(frame);
894 0 : errno = map_errno_from_nt_status(
895 : status);
896 0 : return NULL;
897 : }
898 : } else {
899 : /* Neither the workgroup nor server exists */
900 0 : errno = ECONNREFUSED;
901 0 : if (dir) {
902 0 : SAFE_FREE(dir->fname);
903 0 : SAFE_FREE(dir);
904 : }
905 0 : TALLOC_FREE(frame);
906 0 : return NULL;
907 : }
908 :
909 : }
910 : else {
911 : /*
912 : * The server and share are specified ... work from
913 : * there ...
914 : */
915 : char *targetpath;
916 : struct cli_state *targetcli;
917 42 : struct cli_credentials *creds = NULL;
918 : NTSTATUS status;
919 :
920 : /* We connect to the server and list the directory */
921 42 : dir->dir_type = SMBC_FILE_SHARE;
922 :
923 42 : srv = SMBC_server(frame, context, True, server, port, share,
924 : &workgroup, &user, &password);
925 :
926 42 : if (!srv) {
927 0 : if (dir) {
928 0 : SAFE_FREE(dir->fname);
929 0 : SAFE_FREE(dir);
930 : }
931 0 : TALLOC_FREE(frame);
932 0 : return NULL;
933 : }
934 :
935 42 : dir->srv = srv;
936 :
937 : /* Now, list the files ... */
938 :
939 42 : path_len = strlen(path);
940 42 : path = talloc_asprintf_append(path, "\\*");
941 42 : if (!path) {
942 0 : if (dir) {
943 0 : SAFE_FREE(dir->fname);
944 0 : SAFE_FREE(dir);
945 : }
946 0 : TALLOC_FREE(frame);
947 0 : return NULL;
948 : }
949 :
950 42 : creds = context->internal->creds;
951 :
952 42 : status = cli_resolve_path(
953 : frame, "",
954 : creds,
955 : srv->cli, path, &targetcli, &targetpath);
956 42 : if (!NT_STATUS_IS_OK(status)) {
957 0 : d_printf("Could not resolve %s\n", path);
958 0 : if (dir) {
959 0 : SAFE_FREE(dir->fname);
960 0 : SAFE_FREE(dir);
961 : }
962 0 : TALLOC_FREE(frame);
963 0 : return NULL;
964 : }
965 :
966 42 : status = cli_list(targetcli, targetpath,
967 : FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
968 : dir_list_fn, (void *)dir);
969 42 : if (!NT_STATUS_IS_OK(status)) {
970 : int saved_errno;
971 0 : if (dir) {
972 0 : SAFE_FREE(dir->fname);
973 0 : SAFE_FREE(dir);
974 : }
975 0 : saved_errno = SMBC_errno(context, targetcli);
976 :
977 0 : if (saved_errno == EINVAL) {
978 0 : struct stat sb = {0};
979 : /*
980 : * See if they asked to opendir
981 : * something other than a directory.
982 : * If so, the converted error value we
983 : * got would have been EINVAL rather
984 : * than ENOTDIR.
985 : */
986 0 : path[path_len] = '\0'; /* restore original path */
987 :
988 0 : if (SMBC_getatr(context,
989 : srv,
990 : path,
991 0 : &sb) &&
992 0 : !S_ISDIR(sb.st_mode)) {
993 :
994 : /* It is. Correct the error value */
995 0 : saved_errno = ENOTDIR;
996 : }
997 : }
998 :
999 : /*
1000 : * If there was an error and the server is no
1001 : * good any more...
1002 : */
1003 0 : if (cli_is_error(targetcli) &&
1004 0 : smbc_getFunctionCheckServer(context)(context, srv)) {
1005 :
1006 : /* ... then remove it. */
1007 0 : if (smbc_getFunctionRemoveUnusedServer(context)(context,
1008 : srv)) {
1009 : /*
1010 : * We could not remove the
1011 : * server completely, remove
1012 : * it from the cache so we
1013 : * will not get it again. It
1014 : * will be removed when the
1015 : * last file/dir is closed.
1016 : */
1017 0 : smbc_getFunctionRemoveCachedServer(context)(context, srv);
1018 : }
1019 : }
1020 :
1021 0 : TALLOC_FREE(frame);
1022 0 : errno = saved_errno;
1023 0 : return NULL;
1024 : }
1025 : }
1026 :
1027 : }
1028 :
1029 52 : DLIST_ADD(context->internal->files, dir);
1030 52 : TALLOC_FREE(frame);
1031 52 : return dir;
1032 :
1033 : }
1034 :
1035 : /*
1036 : * Routine to close a directory
1037 : */
1038 :
1039 : int
1040 52 : SMBC_closedir_ctx(SMBCCTX *context,
1041 : SMBCFILE *dir)
1042 : {
1043 52 : TALLOC_CTX *frame = NULL;
1044 :
1045 52 : if (!context || !context->internal->initialized) {
1046 0 : errno = EINVAL;
1047 0 : return -1;
1048 : }
1049 :
1050 52 : if (dir == NULL) {
1051 0 : return 0;
1052 : }
1053 :
1054 52 : frame = talloc_stackframe();
1055 :
1056 52 : if (!SMBC_dlist_contains(context->internal->files, dir)) {
1057 0 : errno = EBADF;
1058 0 : TALLOC_FREE(frame);
1059 0 : return -1;
1060 : }
1061 :
1062 52 : remove_dir(dir); /* Clean it up */
1063 52 : remove_dirplus(dir);
1064 :
1065 52 : DLIST_REMOVE(context->internal->files, dir);
1066 :
1067 52 : SAFE_FREE(dir->fname);
1068 52 : SAFE_FREE(dir); /* Free the space too */
1069 :
1070 52 : TALLOC_FREE(frame);
1071 52 : return 0;
1072 :
1073 : }
1074 :
1075 : static int
1076 834 : smbc_readdir_internal(SMBCCTX * context,
1077 : struct smbc_dirent *dest,
1078 : struct smbc_dirent *src,
1079 : int max_namebuf_len)
1080 : {
1081 834 : if (smbc_getOptionUrlEncodeReaddirEntries(context)) {
1082 : int remaining_len;
1083 :
1084 : /* url-encode the name. get back remaining buffer space */
1085 0 : remaining_len =
1086 0 : smbc_urlencode(dest->name, src->name, max_namebuf_len);
1087 :
1088 : /* -1 means no null termination. */
1089 0 : if (remaining_len < 0) {
1090 0 : return -1;
1091 : }
1092 :
1093 : /* We now know the name length */
1094 0 : dest->namelen = strlen(dest->name);
1095 :
1096 0 : if (dest->namelen + 1 < 1) {
1097 : /* Integer wrap. */
1098 0 : return -1;
1099 : }
1100 :
1101 0 : if (dest->namelen + 1 >= max_namebuf_len) {
1102 : /* Out of space for comment. */
1103 0 : return -1;
1104 : }
1105 :
1106 : /* Save the pointer to the beginning of the comment */
1107 0 : dest->comment = dest->name + dest->namelen + 1;
1108 :
1109 0 : if (remaining_len < 1) {
1110 : /* No room for comment null termination. */
1111 0 : return -1;
1112 : }
1113 :
1114 : /* Copy the comment */
1115 0 : strlcpy(dest->comment, src->comment, remaining_len);
1116 :
1117 : /* Save other fields */
1118 0 : dest->smbc_type = src->smbc_type;
1119 0 : dest->commentlen = strlen(dest->comment);
1120 0 : dest->dirlen = ((dest->comment + dest->commentlen + 1) -
1121 : (char *) dest);
1122 : } else {
1123 :
1124 : /* No encoding. Just copy the entry as is. */
1125 834 : if (src->dirlen > max_namebuf_len) {
1126 0 : return -1;
1127 : }
1128 834 : memcpy(dest, src, src->dirlen);
1129 834 : if (src->namelen + 1 < 1) {
1130 : /* Integer wrap */
1131 0 : return -1;
1132 : }
1133 834 : if (src->namelen + 1 >= max_namebuf_len) {
1134 : /* Comment off the end. */
1135 0 : return -1;
1136 : }
1137 834 : dest->comment = (char *)(&dest->name + src->namelen + 1);
1138 : }
1139 834 : return 0;
1140 : }
1141 :
1142 : /*
1143 : * Routine to get a directory entry
1144 : */
1145 :
1146 : struct smbc_dirent *
1147 708 : SMBC_readdir_ctx(SMBCCTX *context,
1148 : SMBCFILE *dir)
1149 : {
1150 : int maxlen;
1151 : int ret;
1152 : struct smbc_dirent *dirp, *dirent;
1153 708 : TALLOC_CTX *frame = talloc_stackframe();
1154 :
1155 : /* Check that all is ok first ... */
1156 :
1157 708 : if (!context || !context->internal->initialized) {
1158 :
1159 0 : errno = EINVAL;
1160 0 : DEBUG(0, ("Invalid context in SMBC_readdir_ctx()\n"));
1161 0 : TALLOC_FREE(frame);
1162 0 : return NULL;
1163 :
1164 : }
1165 :
1166 708 : if (!SMBC_dlist_contains(context->internal->files, dir)) {
1167 :
1168 0 : errno = EBADF;
1169 0 : DEBUG(0, ("Invalid dir in SMBC_readdir_ctx()\n"));
1170 0 : TALLOC_FREE(frame);
1171 0 : return NULL;
1172 :
1173 : }
1174 :
1175 708 : if (dir->file != False) { /* FIXME, should be dir, perhaps */
1176 :
1177 0 : errno = ENOTDIR;
1178 0 : DEBUG(0, ("Found file vs directory in SMBC_readdir_ctx()\n"));
1179 0 : TALLOC_FREE(frame);
1180 0 : return NULL;
1181 :
1182 : }
1183 :
1184 708 : if (!dir->dir_next) {
1185 34 : TALLOC_FREE(frame);
1186 34 : return NULL;
1187 : }
1188 :
1189 674 : dirent = dir->dir_next->dirent;
1190 674 : if (!dirent) {
1191 :
1192 0 : errno = ENOENT;
1193 0 : TALLOC_FREE(frame);
1194 0 : return NULL;
1195 :
1196 : }
1197 :
1198 674 : dirp = &context->internal->dirent;
1199 674 : maxlen = sizeof(context->internal->_dirent_name);
1200 :
1201 674 : ret = smbc_readdir_internal(context, dirp, dirent, maxlen);
1202 674 : if (ret == -1) {
1203 0 : errno = EINVAL;
1204 0 : TALLOC_FREE(frame);
1205 0 : return NULL;
1206 : }
1207 :
1208 674 : dir->dir_next = dir->dir_next->next;
1209 :
1210 : /*
1211 : * If we are returning file entries, we
1212 : * have a duplicate list in dirplus.
1213 : *
1214 : * Update dirplus_next also so readdir and
1215 : * readdirplus are kept in sync.
1216 : */
1217 674 : if (dir->dirplus_list != NULL) {
1218 106 : dir->dirplus_next = dir->dirplus_next->next;
1219 : }
1220 :
1221 674 : TALLOC_FREE(frame);
1222 674 : return dirp;
1223 : }
1224 :
1225 : /*
1226 : * Routine to get a directory entry with all attributes
1227 : */
1228 :
1229 : const struct libsmb_file_info *
1230 902 : SMBC_readdirplus_ctx(SMBCCTX *context,
1231 : SMBCFILE *dir)
1232 : {
1233 902 : struct libsmb_file_info *smb_finfo = NULL;
1234 902 : TALLOC_CTX *frame = talloc_stackframe();
1235 :
1236 : /* Check that all is ok first ... */
1237 :
1238 902 : if (context == NULL || !context->internal->initialized) {
1239 0 : DBG_ERR("Invalid context in SMBC_readdirplus_ctx()\n");
1240 0 : TALLOC_FREE(frame);
1241 0 : errno = EINVAL;
1242 0 : return NULL;
1243 : }
1244 :
1245 902 : if (!SMBC_dlist_contains(context->internal->files, dir)) {
1246 0 : DBG_ERR("Invalid dir in SMBC_readdirplus_ctx()\n");
1247 0 : TALLOC_FREE(frame);
1248 0 : errno = EBADF;
1249 0 : return NULL;
1250 : }
1251 :
1252 902 : if (dir->dirplus_next == NULL) {
1253 0 : TALLOC_FREE(frame);
1254 0 : return NULL;
1255 : }
1256 :
1257 902 : smb_finfo = dir->dirplus_next->smb_finfo;
1258 902 : if (smb_finfo == NULL) {
1259 0 : TALLOC_FREE(frame);
1260 0 : errno = ENOENT;
1261 0 : return NULL;
1262 : }
1263 902 : dir->dirplus_next = dir->dirplus_next->next;
1264 :
1265 : /*
1266 : * If we are returning file entries, we
1267 : * have a duplicate list in dir_list
1268 : *
1269 : * Update dir_next also so readdir and
1270 : * readdirplus are kept in sync.
1271 : */
1272 902 : if (dir->dir_list) {
1273 902 : dir->dir_next = dir->dir_next->next;
1274 : }
1275 :
1276 902 : TALLOC_FREE(frame);
1277 902 : return smb_finfo;
1278 : }
1279 :
1280 : /*
1281 : * Routine to get a directory entry plus a filled in stat structure if
1282 : * requested.
1283 : */
1284 :
1285 290 : const struct libsmb_file_info *SMBC_readdirplus2_ctx(SMBCCTX *context,
1286 : SMBCFILE *dir,
1287 : struct stat *st)
1288 : {
1289 290 : struct libsmb_file_info *smb_finfo = NULL;
1290 290 : struct smbc_dirplus_list *dp_list = NULL;
1291 : ino_t ino;
1292 290 : char *full_pathname = NULL;
1293 290 : char *workgroup = NULL;
1294 290 : char *server = NULL;
1295 290 : uint16_t port = 0;
1296 290 : char *share = NULL;
1297 290 : char *path = NULL;
1298 290 : char *user = NULL;
1299 290 : char *password = NULL;
1300 290 : char *options = NULL;
1301 : int rc;
1302 290 : TALLOC_CTX *frame = NULL;
1303 :
1304 : /*
1305 : * Allow caller to pass in NULL for stat pointer if
1306 : * required. This makes this call identical to
1307 : * smbc_readdirplus().
1308 : */
1309 :
1310 290 : if (st == NULL) {
1311 0 : return SMBC_readdirplus_ctx(context, dir);
1312 : }
1313 :
1314 290 : frame = talloc_stackframe();
1315 :
1316 : /* Check that all is ok first ... */
1317 290 : if (context == NULL || !context->internal->initialized) {
1318 0 : DBG_ERR("Invalid context in SMBC_readdirplus2_ctx()\n");
1319 0 : TALLOC_FREE(frame);
1320 0 : errno = EINVAL;
1321 0 : return NULL;
1322 : }
1323 :
1324 290 : if (!SMBC_dlist_contains(context->internal->files, dir)) {
1325 0 : DBG_ERR("Invalid dir in SMBC_readdirplus2_ctx()\n");
1326 0 : TALLOC_FREE(frame);
1327 0 : errno = EBADF;
1328 0 : return NULL;
1329 : }
1330 :
1331 290 : dp_list = dir->dirplus_next;
1332 290 : if (dp_list == NULL) {
1333 0 : TALLOC_FREE(frame);
1334 0 : return NULL;
1335 : }
1336 :
1337 290 : ino = (ino_t)dp_list->ino;
1338 :
1339 290 : smb_finfo = dp_list->smb_finfo;
1340 290 : if (smb_finfo == NULL) {
1341 0 : TALLOC_FREE(frame);
1342 0 : errno = ENOENT;
1343 0 : return NULL;
1344 : }
1345 :
1346 290 : full_pathname = talloc_asprintf(frame,
1347 : "%s/%s",
1348 : dir->fname,
1349 : smb_finfo->name);
1350 290 : if (full_pathname == NULL) {
1351 0 : TALLOC_FREE(frame);
1352 0 : errno = ENOENT;
1353 0 : return NULL;
1354 : }
1355 :
1356 290 : rc = SMBC_parse_path(frame,
1357 : context,
1358 : full_pathname,
1359 : &workgroup,
1360 : &server,
1361 : &port,
1362 : &share,
1363 : &path,
1364 : &user,
1365 : &password,
1366 : &options);
1367 290 : if (rc != 0) {
1368 0 : TALLOC_FREE(frame);
1369 0 : errno = ENOENT;
1370 0 : return NULL;
1371 : }
1372 :
1373 870 : setup_stat(st,
1374 : path,
1375 290 : smb_finfo->size,
1376 290 : smb_finfo->attrs,
1377 : ino,
1378 290 : dir->srv->dev,
1379 : smb_finfo->atime_ts,
1380 : smb_finfo->ctime_ts,
1381 : smb_finfo->mtime_ts);
1382 :
1383 290 : TALLOC_FREE(full_pathname);
1384 :
1385 290 : dir->dirplus_next = dir->dirplus_next->next;
1386 :
1387 : /*
1388 : * If we are returning file entries, we
1389 : * have a duplicate list in dir_list
1390 : *
1391 : * Update dir_next also so readdir and
1392 : * readdirplus are kept in sync.
1393 : */
1394 290 : if (dir->dir_list) {
1395 290 : dir->dir_next = dir->dir_next->next;
1396 : }
1397 :
1398 290 : TALLOC_FREE(frame);
1399 290 : return smb_finfo;
1400 : }
1401 :
1402 : /*
1403 : * Routine to get directory entries
1404 : */
1405 :
1406 : int
1407 8 : SMBC_getdents_ctx(SMBCCTX *context,
1408 : SMBCFILE *dir,
1409 : struct smbc_dirent *dirp,
1410 : int count)
1411 : {
1412 8 : int rem = count;
1413 : int reqd;
1414 : int maxlen;
1415 8 : char *ndir = (char *)dirp;
1416 : struct smbc_dir_list *dirlist;
1417 8 : TALLOC_CTX *frame = talloc_stackframe();
1418 :
1419 : /* Check that all is ok first ... */
1420 :
1421 8 : if (!context || !context->internal->initialized) {
1422 :
1423 0 : errno = EINVAL;
1424 0 : TALLOC_FREE(frame);
1425 0 : return -1;
1426 :
1427 : }
1428 :
1429 8 : if (!SMBC_dlist_contains(context->internal->files, dir)) {
1430 :
1431 0 : errno = EBADF;
1432 0 : TALLOC_FREE(frame);
1433 0 : return -1;
1434 :
1435 : }
1436 :
1437 8 : if (dir->file != False) { /* FIXME, should be dir, perhaps */
1438 :
1439 0 : errno = ENOTDIR;
1440 0 : TALLOC_FREE(frame);
1441 0 : return -1;
1442 :
1443 : }
1444 :
1445 : /*
1446 : * Now, retrieve the number of entries that will fit in what was passed
1447 : * We have to figure out if the info is in the list, or we need to
1448 : * send a request to the server to get the info.
1449 : */
1450 :
1451 168 : while ((dirlist = dir->dir_next)) {
1452 : int ret;
1453 : struct smbc_dirent *dirent;
1454 160 : struct smbc_dirent *currentEntry = (struct smbc_dirent *)ndir;
1455 :
1456 160 : if (!dirlist->dirent) {
1457 :
1458 0 : errno = ENOENT; /* Bad error */
1459 0 : TALLOC_FREE(frame);
1460 0 : return -1;
1461 :
1462 : }
1463 :
1464 : /* Do urlencoding of next entry, if so selected */
1465 160 : dirent = &context->internal->dirent;
1466 160 : maxlen = sizeof(context->internal->_dirent_name);
1467 160 : ret = smbc_readdir_internal(context, dirent,
1468 : dirlist->dirent, maxlen);
1469 160 : if (ret == -1) {
1470 0 : errno = EINVAL;
1471 0 : TALLOC_FREE(frame);
1472 0 : return -1;
1473 : }
1474 :
1475 160 : reqd = dirent->dirlen;
1476 :
1477 160 : if (rem < reqd) {
1478 :
1479 8 : if (rem < count) { /* We managed to copy something */
1480 :
1481 8 : errno = 0;
1482 8 : TALLOC_FREE(frame);
1483 8 : return count - rem;
1484 :
1485 : }
1486 : else { /* Nothing copied ... */
1487 :
1488 0 : errno = EINVAL; /* Not enough space ... */
1489 0 : TALLOC_FREE(frame);
1490 0 : return -1;
1491 :
1492 : }
1493 :
1494 : }
1495 :
1496 152 : memcpy(currentEntry, dirent, reqd); /* Copy the data in ... */
1497 :
1498 304 : currentEntry->comment = ¤tEntry->name[0] +
1499 152 : dirent->namelen + 1;
1500 :
1501 152 : ndir += reqd;
1502 152 : rem -= reqd;
1503 :
1504 : /* Try and align the struct for the next entry
1505 : on a valid pointer boundary by appending zeros */
1506 1068 : while((rem > 0) && ((uintptr_t)ndir & (sizeof(void*) - 1))) {
1507 764 : *ndir = '\0';
1508 764 : rem--;
1509 764 : ndir++;
1510 764 : currentEntry->dirlen++;
1511 : }
1512 :
1513 152 : dir->dir_next = dirlist = dirlist -> next;
1514 :
1515 : /*
1516 : * If we are returning file entries, we
1517 : * have a duplicate list in dirplus.
1518 : *
1519 : * Update dirplus_next also so readdir and
1520 : * readdirplus are kept in sync.
1521 : */
1522 152 : if (dir->dirplus_list != NULL) {
1523 152 : dir->dirplus_next = dir->dirplus_next->next;
1524 : }
1525 : }
1526 :
1527 0 : TALLOC_FREE(frame);
1528 :
1529 0 : if (rem == count)
1530 0 : return 0;
1531 : else
1532 0 : return count - rem;
1533 :
1534 : }
1535 :
1536 : /*
1537 : * Routine to create a directory ...
1538 : */
1539 :
1540 : int
1541 4 : SMBC_mkdir_ctx(SMBCCTX *context,
1542 : const char *fname,
1543 : mode_t mode)
1544 : {
1545 4 : SMBCSRV *srv = NULL;
1546 4 : char *server = NULL;
1547 4 : char *share = NULL;
1548 4 : char *user = NULL;
1549 4 : char *password = NULL;
1550 4 : char *workgroup = NULL;
1551 4 : char *path = NULL;
1552 4 : char *targetpath = NULL;
1553 4 : uint16_t port = 0;
1554 4 : struct cli_state *targetcli = NULL;
1555 4 : struct cli_credentials *creds = NULL;
1556 4 : TALLOC_CTX *frame = talloc_stackframe();
1557 : NTSTATUS status;
1558 :
1559 4 : if (!context || !context->internal->initialized) {
1560 0 : errno = EINVAL;
1561 0 : TALLOC_FREE(frame);
1562 0 : return -1;
1563 : }
1564 :
1565 4 : if (!fname) {
1566 0 : errno = EINVAL;
1567 0 : TALLOC_FREE(frame);
1568 0 : return -1;
1569 : }
1570 :
1571 4 : DEBUG(4, ("smbc_mkdir(%s)\n", fname));
1572 :
1573 4 : if (SMBC_parse_path(frame,
1574 : context,
1575 : fname,
1576 : &workgroup,
1577 : &server,
1578 : &port,
1579 : &share,
1580 : &path,
1581 : &user,
1582 : &password,
1583 : NULL)) {
1584 0 : errno = EINVAL;
1585 0 : TALLOC_FREE(frame);
1586 0 : return -1;
1587 : }
1588 :
1589 4 : if (!user || user[0] == (char)0) {
1590 0 : user = talloc_strdup(frame, smbc_getUser(context));
1591 0 : if (!user) {
1592 0 : errno = ENOMEM;
1593 0 : TALLOC_FREE(frame);
1594 0 : return -1;
1595 : }
1596 : }
1597 :
1598 4 : srv = SMBC_server(frame, context, True,
1599 : server, port, share, &workgroup, &user, &password);
1600 :
1601 4 : if (!srv) {
1602 :
1603 0 : TALLOC_FREE(frame);
1604 0 : return -1; /* errno set by SMBC_server */
1605 :
1606 : }
1607 :
1608 4 : creds = context->internal->creds;
1609 :
1610 : /*d_printf(">>>mkdir: resolving %s\n", path);*/
1611 4 : status = cli_resolve_path(frame, "",
1612 : creds,
1613 : srv->cli, path, &targetcli, &targetpath);
1614 4 : if (!NT_STATUS_IS_OK(status)) {
1615 0 : d_printf("Could not resolve %s\n", path);
1616 0 : errno = ENOENT;
1617 0 : TALLOC_FREE(frame);
1618 0 : return -1;
1619 : }
1620 : /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
1621 :
1622 4 : status = cli_mkdir(targetcli, targetpath);
1623 4 : if (!NT_STATUS_IS_OK(status)) {
1624 0 : TALLOC_FREE(frame);
1625 0 : errno = cli_status_to_errno(status);
1626 0 : return -1;
1627 :
1628 : }
1629 :
1630 4 : TALLOC_FREE(frame);
1631 4 : return 0;
1632 :
1633 : }
1634 :
1635 : /*
1636 : * Our list function simply checks to see if a directory is not empty
1637 : */
1638 :
1639 : static NTSTATUS
1640 0 : rmdir_list_fn(struct file_info *finfo,
1641 : const char *mask,
1642 : void *state)
1643 : {
1644 0 : if (strncmp(finfo->name, ".", 1) != 0 &&
1645 0 : strncmp(finfo->name, "..", 2) != 0) {
1646 0 : bool *smbc_rmdir_dirempty = (bool *)state;
1647 0 : *smbc_rmdir_dirempty = false;
1648 : }
1649 0 : return NT_STATUS_OK;
1650 : }
1651 :
1652 : /*
1653 : * Routine to remove a directory
1654 : */
1655 :
1656 : int
1657 8 : SMBC_rmdir_ctx(SMBCCTX *context,
1658 : const char *fname)
1659 : {
1660 8 : SMBCSRV *srv = NULL;
1661 8 : char *server = NULL;
1662 8 : char *share = NULL;
1663 8 : char *user = NULL;
1664 8 : char *password = NULL;
1665 8 : char *workgroup = NULL;
1666 8 : char *path = NULL;
1667 8 : char *targetpath = NULL;
1668 8 : uint16_t port = 0;
1669 8 : struct cli_state *targetcli = NULL;
1670 8 : struct cli_credentials *creds = NULL;
1671 8 : TALLOC_CTX *frame = talloc_stackframe();
1672 : NTSTATUS status;
1673 :
1674 8 : if (!context || !context->internal->initialized) {
1675 0 : errno = EINVAL;
1676 0 : TALLOC_FREE(frame);
1677 0 : return -1;
1678 : }
1679 :
1680 8 : if (!fname) {
1681 0 : errno = EINVAL;
1682 0 : TALLOC_FREE(frame);
1683 0 : return -1;
1684 : }
1685 :
1686 8 : DEBUG(4, ("smbc_rmdir(%s)\n", fname));
1687 :
1688 8 : if (SMBC_parse_path(frame,
1689 : context,
1690 : fname,
1691 : &workgroup,
1692 : &server,
1693 : &port,
1694 : &share,
1695 : &path,
1696 : &user,
1697 : &password,
1698 : NULL)) {
1699 0 : errno = EINVAL;
1700 0 : TALLOC_FREE(frame);
1701 0 : return -1;
1702 : }
1703 :
1704 8 : if (!user || user[0] == (char)0) {
1705 0 : user = talloc_strdup(frame, smbc_getUser(context));
1706 0 : if (!user) {
1707 0 : errno = ENOMEM;
1708 0 : TALLOC_FREE(frame);
1709 0 : return -1;
1710 : }
1711 : }
1712 :
1713 8 : srv = SMBC_server(frame, context, True,
1714 : server, port, share, &workgroup, &user, &password);
1715 :
1716 8 : if (!srv) {
1717 :
1718 0 : TALLOC_FREE(frame);
1719 0 : return -1; /* errno set by SMBC_server */
1720 :
1721 : }
1722 :
1723 8 : creds = context->internal->creds;
1724 :
1725 : /*d_printf(">>>rmdir: resolving %s\n", path);*/
1726 8 : status = cli_resolve_path(frame, "",
1727 : creds,
1728 : srv->cli, path, &targetcli, &targetpath);
1729 8 : if (!NT_STATUS_IS_OK(status)) {
1730 0 : d_printf("Could not resolve %s\n", path);
1731 0 : errno = ENOENT;
1732 0 : TALLOC_FREE(frame);
1733 0 : return -1;
1734 : }
1735 : /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
1736 :
1737 8 : if (!NT_STATUS_IS_OK(cli_rmdir(targetcli, targetpath))) {
1738 :
1739 4 : errno = SMBC_errno(context, targetcli);
1740 :
1741 4 : if (errno == EACCES) { /* Check if the dir empty or not */
1742 :
1743 : /* Local storage to avoid buffer overflows */
1744 : char *lpath;
1745 0 : bool smbc_rmdir_dirempty = true;
1746 :
1747 0 : lpath = talloc_asprintf(frame, "%s\\*",
1748 : targetpath);
1749 0 : if (!lpath) {
1750 0 : errno = ENOMEM;
1751 0 : TALLOC_FREE(frame);
1752 0 : return -1;
1753 : }
1754 :
1755 0 : status = cli_list(targetcli, lpath,
1756 : FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1757 : rmdir_list_fn,
1758 : &smbc_rmdir_dirempty);
1759 :
1760 0 : if (!NT_STATUS_IS_OK(status)) {
1761 : /* Fix errno to ignore latest error ... */
1762 0 : DEBUG(5, ("smbc_rmdir: "
1763 : "cli_list returned an error: %d\n",
1764 : SMBC_errno(context, targetcli)));
1765 0 : errno = EACCES;
1766 :
1767 : }
1768 :
1769 0 : if (smbc_rmdir_dirempty)
1770 0 : errno = EACCES;
1771 : else
1772 0 : errno = ENOTEMPTY;
1773 :
1774 : }
1775 :
1776 4 : TALLOC_FREE(frame);
1777 4 : return -1;
1778 :
1779 : }
1780 :
1781 4 : TALLOC_FREE(frame);
1782 4 : return 0;
1783 :
1784 : }
1785 :
1786 : /*
1787 : * Routine to return the current directory position
1788 : */
1789 :
1790 : off_t
1791 8 : SMBC_telldir_ctx(SMBCCTX *context,
1792 : SMBCFILE *dir)
1793 : {
1794 8 : TALLOC_CTX *frame = talloc_stackframe();
1795 :
1796 8 : if (!context || !context->internal->initialized) {
1797 :
1798 0 : errno = EINVAL;
1799 0 : TALLOC_FREE(frame);
1800 0 : return -1;
1801 :
1802 : }
1803 :
1804 8 : if (!SMBC_dlist_contains(context->internal->files, dir)) {
1805 :
1806 0 : errno = EBADF;
1807 0 : TALLOC_FREE(frame);
1808 0 : return -1;
1809 :
1810 : }
1811 :
1812 8 : if (dir->file != False) { /* FIXME, should be dir, perhaps */
1813 :
1814 0 : errno = ENOTDIR;
1815 0 : TALLOC_FREE(frame);
1816 0 : return -1;
1817 :
1818 : }
1819 :
1820 : /* See if we're already at the end. */
1821 8 : if (dir->dir_next == NULL) {
1822 : /* We are. */
1823 0 : TALLOC_FREE(frame);
1824 0 : return -1;
1825 : }
1826 :
1827 : /*
1828 : * We return the pointer here as the offset
1829 : */
1830 8 : TALLOC_FREE(frame);
1831 8 : return (off_t)(long)dir->dir_next->dirent;
1832 : }
1833 :
1834 : /*
1835 : * A routine to run down the list and see if the entry is OK
1836 : * Modifies the dir list and the dirplus list (if it exists)
1837 : * to point at the correct next entry on success.
1838 : */
1839 :
1840 16 : static bool update_dir_ents(SMBCFILE *dir, struct smbc_dirent *dirent)
1841 : {
1842 16 : struct smbc_dir_list *tmp_dir = dir->dir_list;
1843 16 : struct smbc_dirplus_list *tmp_dirplus = dir->dirplus_list;
1844 :
1845 : /*
1846 : * Run down the list looking for what we want.
1847 : * If we're enumerating files both dir_list
1848 : * and dirplus_list contain the same entry
1849 : * list, as they were seeded from the same
1850 : * cli_list callback.
1851 : *
1852 : * If we're enumerating servers then
1853 : * dirplus_list will be NULL, so don't
1854 : * update in that case.
1855 : */
1856 :
1857 476 : while (tmp_dir != NULL) {
1858 460 : if (tmp_dir->dirent == dirent) {
1859 16 : dir->dir_next = tmp_dir;
1860 16 : if (tmp_dirplus != NULL) {
1861 16 : dir->dirplus_next = tmp_dirplus;
1862 : }
1863 16 : return true;
1864 : }
1865 444 : tmp_dir = tmp_dir->next;
1866 444 : if (tmp_dirplus != NULL) {
1867 444 : tmp_dirplus = tmp_dirplus->next;
1868 : }
1869 : }
1870 0 : return false;
1871 : }
1872 :
1873 : /*
1874 : * Routine to seek on a directory
1875 : */
1876 :
1877 : int
1878 20 : SMBC_lseekdir_ctx(SMBCCTX *context,
1879 : SMBCFILE *dir,
1880 : off_t offset)
1881 : {
1882 20 : long int l_offset = offset; /* Handle problems of size */
1883 20 : struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
1884 20 : TALLOC_CTX *frame = talloc_stackframe();
1885 : bool ok;
1886 :
1887 20 : if (!context || !context->internal->initialized) {
1888 :
1889 0 : errno = EINVAL;
1890 0 : TALLOC_FREE(frame);
1891 0 : return -1;
1892 :
1893 : }
1894 :
1895 20 : if (dir->file != False) { /* FIXME, should be dir, perhaps */
1896 :
1897 0 : errno = ENOTDIR;
1898 0 : TALLOC_FREE(frame);
1899 0 : return -1;
1900 :
1901 : }
1902 :
1903 : /* Now, check what we were passed and see if it is OK ... */
1904 :
1905 20 : if (dirent == NULL) { /* Seek to the beginning of the list */
1906 :
1907 4 : dir->dir_next = dir->dir_list;
1908 :
1909 : /* Do the same for dirplus. */
1910 4 : dir->dirplus_next = dir->dirplus_list;
1911 :
1912 4 : TALLOC_FREE(frame);
1913 4 : return 0;
1914 :
1915 : }
1916 :
1917 16 : if (offset == -1) { /* Seek to the end of the list */
1918 0 : dir->dir_next = NULL;
1919 :
1920 : /* Do the same for dirplus. */
1921 0 : dir->dirplus_next = NULL;
1922 :
1923 0 : TALLOC_FREE(frame);
1924 0 : return 0;
1925 : }
1926 :
1927 : /*
1928 : * Run down the list and make sure that the entry is OK.
1929 : * Update the position of both dir and dirplus lists.
1930 : */
1931 :
1932 16 : ok = update_dir_ents(dir, dirent);
1933 16 : if (!ok) {
1934 0 : errno = EINVAL; /* Bad entry */
1935 0 : TALLOC_FREE(frame);
1936 0 : return -1;
1937 : }
1938 :
1939 16 : TALLOC_FREE(frame);
1940 16 : return 0;
1941 : }
1942 :
1943 : /*
1944 : * Routine to fstat a dir
1945 : */
1946 :
1947 : int
1948 0 : SMBC_fstatdir_ctx(SMBCCTX *context,
1949 : SMBCFILE *dir,
1950 : struct stat *st)
1951 : {
1952 :
1953 0 : if (!context || !context->internal->initialized) {
1954 :
1955 0 : errno = EINVAL;
1956 0 : return -1;
1957 : }
1958 :
1959 : /* No code yet ... */
1960 0 : return 0;
1961 : }
1962 :
1963 : int
1964 0 : SMBC_chmod_ctx(SMBCCTX *context,
1965 : const char *fname,
1966 : mode_t newmode)
1967 : {
1968 0 : SMBCSRV *srv = NULL;
1969 0 : char *server = NULL;
1970 0 : char *share = NULL;
1971 0 : char *user = NULL;
1972 0 : char *password = NULL;
1973 0 : char *workgroup = NULL;
1974 0 : char *targetpath = NULL;
1975 0 : struct cli_state *targetcli = NULL;
1976 0 : char *path = NULL;
1977 : uint32_t attr;
1978 0 : uint16_t port = 0;
1979 0 : struct cli_credentials *creds = NULL;
1980 0 : TALLOC_CTX *frame = talloc_stackframe();
1981 : NTSTATUS status;
1982 :
1983 0 : if (!context || !context->internal->initialized) {
1984 :
1985 0 : errno = EINVAL; /* Best I can think of ... */
1986 0 : TALLOC_FREE(frame);
1987 0 : return -1;
1988 : }
1989 :
1990 0 : if (!fname) {
1991 0 : errno = EINVAL;
1992 0 : TALLOC_FREE(frame);
1993 0 : return -1;
1994 : }
1995 :
1996 0 : DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, (unsigned int)newmode));
1997 :
1998 0 : if (SMBC_parse_path(frame,
1999 : context,
2000 : fname,
2001 : &workgroup,
2002 : &server,
2003 : &port,
2004 : &share,
2005 : &path,
2006 : &user,
2007 : &password,
2008 : NULL)) {
2009 0 : errno = EINVAL;
2010 0 : TALLOC_FREE(frame);
2011 0 : return -1;
2012 : }
2013 :
2014 0 : if (!user || user[0] == (char)0) {
2015 0 : user = talloc_strdup(frame, smbc_getUser(context));
2016 0 : if (!user) {
2017 0 : errno = ENOMEM;
2018 0 : TALLOC_FREE(frame);
2019 0 : return -1;
2020 : }
2021 : }
2022 :
2023 0 : srv = SMBC_server(frame, context, True,
2024 : server, port, share, &workgroup, &user, &password);
2025 :
2026 0 : if (!srv) {
2027 0 : TALLOC_FREE(frame);
2028 0 : return -1; /* errno set by SMBC_server */
2029 : }
2030 :
2031 0 : creds = context->internal->creds;
2032 :
2033 : /*d_printf(">>>unlink: resolving %s\n", path);*/
2034 0 : status = cli_resolve_path(frame, "",
2035 : creds,
2036 : srv->cli, path, &targetcli, &targetpath);
2037 0 : if (!NT_STATUS_IS_OK(status)) {
2038 0 : d_printf("Could not resolve %s\n", path);
2039 0 : errno = ENOENT;
2040 0 : TALLOC_FREE(frame);
2041 0 : return -1;
2042 : }
2043 :
2044 0 : attr = 0;
2045 :
2046 0 : if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) attr |= FILE_ATTRIBUTE_READONLY;
2047 0 : if ((newmode & S_IXUSR) && lp_map_archive(-1)) attr |= FILE_ATTRIBUTE_ARCHIVE;
2048 0 : if ((newmode & S_IXGRP) && lp_map_system(-1)) attr |= FILE_ATTRIBUTE_SYSTEM;
2049 0 : if ((newmode & S_IXOTH) && lp_map_hidden(-1)) attr |= FILE_ATTRIBUTE_HIDDEN;
2050 :
2051 0 : status = cli_setatr(targetcli, targetpath, attr, 0);
2052 0 : if (!NT_STATUS_IS_OK(status)) {
2053 0 : TALLOC_FREE(frame);
2054 0 : errno = cli_status_to_errno(status);
2055 0 : return -1;
2056 : }
2057 :
2058 0 : TALLOC_FREE(frame);
2059 0 : return 0;
2060 : }
2061 :
2062 : int
2063 4 : SMBC_utimes_ctx(SMBCCTX *context,
2064 : const char *fname,
2065 : struct timeval *tbuf)
2066 : {
2067 4 : SMBCSRV *srv = NULL;
2068 4 : char *server = NULL;
2069 4 : char *share = NULL;
2070 4 : char *user = NULL;
2071 4 : char *password = NULL;
2072 4 : char *workgroup = NULL;
2073 4 : char *path = NULL;
2074 : struct timespec access_time, write_time;
2075 4 : uint16_t port = 0;
2076 4 : TALLOC_CTX *frame = talloc_stackframe();
2077 : bool ok;
2078 :
2079 4 : if (!context || !context->internal->initialized) {
2080 :
2081 0 : errno = EINVAL; /* Best I can think of ... */
2082 0 : TALLOC_FREE(frame);
2083 0 : return -1;
2084 : }
2085 :
2086 4 : if (!fname) {
2087 0 : errno = EINVAL;
2088 0 : TALLOC_FREE(frame);
2089 0 : return -1;
2090 : }
2091 :
2092 4 : if (tbuf == NULL) {
2093 0 : access_time = write_time = timespec_current();
2094 : } else {
2095 4 : access_time = convert_timeval_to_timespec(tbuf[0]);
2096 4 : write_time = convert_timeval_to_timespec(tbuf[1]);
2097 : }
2098 :
2099 4 : if (DEBUGLVL(4)) {
2100 : struct timeval_buf abuf, wbuf;
2101 :
2102 0 : dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
2103 : fname,
2104 : timespec_string_buf(&access_time, false, &abuf),
2105 : timespec_string_buf(&write_time, false, &wbuf));
2106 : }
2107 :
2108 4 : if (SMBC_parse_path(frame,
2109 : context,
2110 : fname,
2111 : &workgroup,
2112 : &server,
2113 : &port,
2114 : &share,
2115 : &path,
2116 : &user,
2117 : &password,
2118 : NULL)) {
2119 0 : errno = EINVAL;
2120 0 : TALLOC_FREE(frame);
2121 0 : return -1;
2122 : }
2123 :
2124 4 : if (!user || user[0] == (char)0) {
2125 0 : user = talloc_strdup(frame, smbc_getUser(context));
2126 0 : if (!user) {
2127 0 : errno = ENOMEM;
2128 0 : TALLOC_FREE(frame);
2129 0 : return -1;
2130 : }
2131 : }
2132 :
2133 4 : srv = SMBC_server(frame, context, True,
2134 : server, port, share, &workgroup, &user, &password);
2135 :
2136 4 : if (!srv) {
2137 0 : TALLOC_FREE(frame);
2138 0 : return -1; /* errno set by SMBC_server */
2139 : }
2140 :
2141 4 : ok = SMBC_setatr(
2142 : context,
2143 : srv,
2144 : path,
2145 4 : (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT },
2146 : access_time,
2147 : write_time,
2148 4 : (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT },
2149 : 0);
2150 4 : if (!ok) {
2151 0 : TALLOC_FREE(frame);
2152 0 : return -1; /* errno set by SMBC_setatr */
2153 : }
2154 :
2155 4 : TALLOC_FREE(frame);
2156 4 : return 0;
2157 : }
2158 :
2159 : /*
2160 : * Routine to unlink() a file
2161 : */
2162 :
2163 : int
2164 820 : SMBC_unlink_ctx(SMBCCTX *context,
2165 : const char *fname)
2166 : {
2167 820 : char *server = NULL;
2168 820 : char *share = NULL;
2169 820 : char *user = NULL;
2170 820 : char *password = NULL;
2171 820 : char *workgroup = NULL;
2172 820 : char *path = NULL;
2173 820 : char *targetpath = NULL;
2174 820 : uint16_t port = 0;
2175 820 : struct cli_state *targetcli = NULL;
2176 820 : SMBCSRV *srv = NULL;
2177 820 : struct cli_credentials *creds = NULL;
2178 820 : TALLOC_CTX *frame = talloc_stackframe();
2179 : NTSTATUS status;
2180 :
2181 820 : if (!context || !context->internal->initialized) {
2182 :
2183 0 : errno = EINVAL; /* Best I can think of ... */
2184 0 : TALLOC_FREE(frame);
2185 0 : return -1;
2186 :
2187 : }
2188 :
2189 820 : if (!fname) {
2190 0 : errno = EINVAL;
2191 0 : TALLOC_FREE(frame);
2192 0 : return -1;
2193 :
2194 : }
2195 :
2196 820 : if (SMBC_parse_path(frame,
2197 : context,
2198 : fname,
2199 : &workgroup,
2200 : &server,
2201 : &port,
2202 : &share,
2203 : &path,
2204 : &user,
2205 : &password,
2206 : NULL)) {
2207 0 : errno = EINVAL;
2208 0 : TALLOC_FREE(frame);
2209 0 : return -1;
2210 : }
2211 :
2212 820 : if (!user || user[0] == (char)0) {
2213 0 : user = talloc_strdup(frame, smbc_getUser(context));
2214 0 : if (!user) {
2215 0 : errno = ENOMEM;
2216 0 : TALLOC_FREE(frame);
2217 0 : return -1;
2218 : }
2219 : }
2220 :
2221 820 : srv = SMBC_server(frame, context, True,
2222 : server, port, share, &workgroup, &user, &password);
2223 :
2224 820 : if (!srv) {
2225 0 : TALLOC_FREE(frame);
2226 0 : return -1; /* SMBC_server sets errno */
2227 :
2228 : }
2229 :
2230 820 : creds = context->internal->creds;
2231 :
2232 : /*d_printf(">>>unlink: resolving %s\n", path);*/
2233 820 : status = cli_resolve_path(frame, "",
2234 : creds,
2235 : srv->cli, path, &targetcli, &targetpath);
2236 820 : if (!NT_STATUS_IS_OK(status)) {
2237 0 : d_printf("Could not resolve %s\n", path);
2238 0 : errno = ENOENT;
2239 0 : TALLOC_FREE(frame);
2240 0 : return -1;
2241 : }
2242 : /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
2243 :
2244 820 : if (!NT_STATUS_IS_OK(cli_unlink(targetcli, targetpath, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN))) {
2245 :
2246 408 : errno = SMBC_errno(context, targetcli);
2247 :
2248 408 : if (errno == EACCES) { /* Check if the file is a directory */
2249 :
2250 0 : int saverr = errno;
2251 0 : struct stat sb = {0};
2252 : bool ok;
2253 :
2254 0 : ok = SMBC_getatr(context, srv, path, &sb);
2255 0 : if (!ok) {
2256 : /* Hmmm, bad error ... What? */
2257 :
2258 0 : errno = SMBC_errno(context, targetcli);
2259 0 : TALLOC_FREE(frame);
2260 0 : return -1;
2261 :
2262 : }
2263 : else {
2264 :
2265 0 : if (S_ISDIR(sb.st_mode))
2266 0 : errno = EISDIR;
2267 : else
2268 0 : errno = saverr; /* Restore this */
2269 :
2270 : }
2271 : }
2272 :
2273 408 : TALLOC_FREE(frame);
2274 408 : return -1;
2275 :
2276 : }
2277 :
2278 412 : TALLOC_FREE(frame);
2279 412 : return 0; /* Success ... */
2280 :
2281 : }
2282 :
2283 : /*
2284 : * Routine to rename() a file
2285 : */
2286 :
2287 : int
2288 0 : SMBC_rename_ctx(SMBCCTX *ocontext,
2289 : const char *oname,
2290 : SMBCCTX *ncontext,
2291 : const char *nname)
2292 : {
2293 0 : char *server1 = NULL;
2294 0 : char *share1 = NULL;
2295 0 : char *server2 = NULL;
2296 0 : char *share2 = NULL;
2297 0 : char *user1 = NULL;
2298 0 : char *user2 = NULL;
2299 0 : char *password1 = NULL;
2300 0 : char *password2 = NULL;
2301 0 : char *workgroup = NULL;
2302 0 : char *path1 = NULL;
2303 0 : char *path2 = NULL;
2304 0 : char *targetpath1 = NULL;
2305 0 : char *targetpath2 = NULL;
2306 0 : struct cli_state *targetcli1 = NULL;
2307 0 : struct cli_state *targetcli2 = NULL;
2308 0 : SMBCSRV *srv = NULL;
2309 0 : uint16_t port1 = 0;
2310 0 : uint16_t port2 = 0;
2311 0 : struct cli_credentials *ocreds = NULL;
2312 0 : struct cli_credentials *ncreds = NULL;
2313 0 : TALLOC_CTX *frame = talloc_stackframe();
2314 : NTSTATUS status;
2315 :
2316 0 : if (!ocontext || !ncontext ||
2317 0 : !ocontext->internal->initialized ||
2318 0 : !ncontext->internal->initialized) {
2319 :
2320 0 : errno = EINVAL; /* Best I can think of ... */
2321 0 : TALLOC_FREE(frame);
2322 0 : return -1;
2323 : }
2324 :
2325 0 : if (!oname || !nname) {
2326 0 : errno = EINVAL;
2327 0 : TALLOC_FREE(frame);
2328 0 : return -1;
2329 : }
2330 :
2331 0 : DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
2332 :
2333 0 : if (SMBC_parse_path(frame,
2334 : ocontext,
2335 : oname,
2336 : &workgroup,
2337 : &server1,
2338 : &port1,
2339 : &share1,
2340 : &path1,
2341 : &user1,
2342 : &password1,
2343 : NULL)) {
2344 0 : errno = EINVAL;
2345 0 : TALLOC_FREE(frame);
2346 0 : return -1;
2347 : }
2348 :
2349 0 : if (!user1 || user1[0] == (char)0) {
2350 0 : user1 = talloc_strdup(frame, smbc_getUser(ocontext));
2351 0 : if (!user1) {
2352 0 : errno = ENOMEM;
2353 0 : TALLOC_FREE(frame);
2354 0 : return -1;
2355 : }
2356 : }
2357 :
2358 0 : if (SMBC_parse_path(frame,
2359 : ncontext,
2360 : nname,
2361 : NULL,
2362 : &server2,
2363 : &port2,
2364 : &share2,
2365 : &path2,
2366 : &user2,
2367 : &password2,
2368 : NULL)) {
2369 0 : errno = EINVAL;
2370 0 : TALLOC_FREE(frame);
2371 0 : return -1;
2372 : }
2373 :
2374 0 : if (!user2 || user2[0] == (char)0) {
2375 0 : user2 = talloc_strdup(frame, smbc_getUser(ncontext));
2376 0 : if (!user2) {
2377 0 : errno = ENOMEM;
2378 0 : TALLOC_FREE(frame);
2379 0 : return -1;
2380 : }
2381 : }
2382 :
2383 0 : if (strcmp(server1, server2) || strcmp(share1, share2) ||
2384 0 : strcmp(user1, user2)) {
2385 : /* Can't rename across file systems, or users?? */
2386 0 : errno = EXDEV;
2387 0 : TALLOC_FREE(frame);
2388 0 : return -1;
2389 : }
2390 :
2391 0 : srv = SMBC_server(frame, ocontext, True,
2392 : server1, port1, share1, &workgroup, &user1, &password1);
2393 0 : if (!srv) {
2394 0 : TALLOC_FREE(frame);
2395 0 : return -1;
2396 :
2397 : }
2398 :
2399 : /* set the credentials to make DFS work */
2400 0 : smbc_set_credentials_with_fallback(ocontext,
2401 : workgroup,
2402 : user1,
2403 : password1);
2404 :
2405 : /*d_printf(">>>rename: resolving %s\n", path1);*/
2406 0 : ocreds = ocontext->internal->creds;
2407 :
2408 0 : status = cli_resolve_path(frame, "",
2409 : ocreds,
2410 : srv->cli, path1, &targetcli1, &targetpath1);
2411 0 : if (!NT_STATUS_IS_OK(status)) {
2412 0 : d_printf("Could not resolve %s\n", path1);
2413 0 : errno = ENOENT;
2414 0 : TALLOC_FREE(frame);
2415 0 : return -1;
2416 : }
2417 :
2418 : /* set the credentials to make DFS work */
2419 0 : smbc_set_credentials_with_fallback(ncontext,
2420 : workgroup,
2421 : user2,
2422 : password2);
2423 :
2424 : /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
2425 : /*d_printf(">>>rename: resolving %s\n", path2);*/
2426 0 : ncreds = ncontext->internal->creds;
2427 :
2428 0 : status = cli_resolve_path(frame, "",
2429 : ncreds,
2430 : srv->cli, path2, &targetcli2, &targetpath2);
2431 0 : if (!NT_STATUS_IS_OK(status)) {
2432 0 : d_printf("Could not resolve %s\n", path2);
2433 0 : errno = ENOENT;
2434 0 : TALLOC_FREE(frame);
2435 0 : return -1;
2436 : }
2437 : /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
2438 :
2439 0 : if (strcmp(smbXcli_conn_remote_name(targetcli1->conn), smbXcli_conn_remote_name(targetcli2->conn)) ||
2440 0 : strcmp(targetcli1->share, targetcli2->share))
2441 : {
2442 : /* can't rename across file systems */
2443 0 : errno = EXDEV;
2444 0 : TALLOC_FREE(frame);
2445 0 : return -1;
2446 : }
2447 :
2448 0 : if (!NT_STATUS_IS_OK(
2449 : cli_rename(targetcli1, targetpath1, targetpath2, false))) {
2450 0 : int eno = SMBC_errno(ocontext, targetcli1);
2451 :
2452 0 : if (eno != EEXIST ||
2453 0 : !NT_STATUS_IS_OK(cli_unlink(targetcli1, targetpath2,
2454 : FILE_ATTRIBUTE_SYSTEM |
2455 0 : FILE_ATTRIBUTE_HIDDEN)) ||
2456 0 : !NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1,
2457 : targetpath2, false))) {
2458 :
2459 0 : errno = eno;
2460 0 : TALLOC_FREE(frame);
2461 0 : return -1;
2462 :
2463 : }
2464 : }
2465 :
2466 0 : TALLOC_FREE(frame);
2467 0 : return 0; /* Success */
2468 : }
2469 :
2470 : struct smbc_notify_cb_state {
2471 : struct tevent_context *ev;
2472 : struct cli_state *cli;
2473 : uint16_t fnum;
2474 : bool recursive;
2475 : uint32_t completion_filter;
2476 : unsigned callback_timeout_ms;
2477 : smbc_notify_callback_fn cb;
2478 : void *private_data;
2479 : };
2480 :
2481 : static void smbc_notify_cb_got_changes(struct tevent_req *subreq);
2482 : static void smbc_notify_cb_timedout(struct tevent_req *subreq);
2483 :
2484 0 : static struct tevent_req *smbc_notify_cb_send(
2485 : TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
2486 : uint16_t fnum, bool recursive, uint32_t completion_filter,
2487 : unsigned callback_timeout_ms,
2488 : smbc_notify_callback_fn cb, void *private_data)
2489 : {
2490 : struct tevent_req *req, *subreq;
2491 : struct smbc_notify_cb_state *state;
2492 :
2493 0 : req = tevent_req_create(mem_ctx, &state, struct smbc_notify_cb_state);
2494 0 : if (req == NULL) {
2495 0 : return NULL;
2496 : }
2497 0 : state->ev = ev;
2498 0 : state->cli = cli;
2499 0 : state->fnum = fnum;
2500 0 : state->recursive = recursive;
2501 0 : state->completion_filter = completion_filter;
2502 0 : state->callback_timeout_ms = callback_timeout_ms;
2503 0 : state->cb = cb;
2504 0 : state->private_data = private_data;
2505 :
2506 0 : subreq = cli_notify_send(
2507 0 : state, state->ev, state->cli, state->fnum, 1000,
2508 0 : state->completion_filter, state->recursive);
2509 0 : if (tevent_req_nomem(subreq, req)) {
2510 0 : return tevent_req_post(req, ev);
2511 : }
2512 0 : tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req);
2513 :
2514 0 : if (state->callback_timeout_ms == 0) {
2515 0 : return req;
2516 : }
2517 :
2518 0 : subreq = tevent_wakeup_send(
2519 0 : state, state->ev,
2520 0 : tevent_timeval_current_ofs(state->callback_timeout_ms/1000,
2521 0 : state->callback_timeout_ms*1000));
2522 0 : if (tevent_req_nomem(subreq, req)) {
2523 0 : return tevent_req_post(req, ev);
2524 : }
2525 0 : tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req);
2526 :
2527 0 : return req;
2528 : }
2529 :
2530 0 : static void smbc_notify_cb_got_changes(struct tevent_req *subreq)
2531 : {
2532 0 : struct tevent_req *req = tevent_req_callback_data(
2533 : subreq, struct tevent_req);
2534 0 : struct smbc_notify_cb_state *state = tevent_req_data(
2535 : req, struct smbc_notify_cb_state);
2536 : uint32_t num_changes;
2537 : struct notify_change *changes;
2538 : NTSTATUS status;
2539 : int cb_ret;
2540 :
2541 0 : status = cli_notify_recv(subreq, state, &num_changes, &changes);
2542 0 : TALLOC_FREE(subreq);
2543 0 : if (tevent_req_nterror(req, status)) {
2544 0 : return;
2545 : }
2546 :
2547 0 : {
2548 0 : struct smbc_notify_callback_action actions[num_changes];
2549 : uint32_t i;
2550 :
2551 0 : for (i=0; i<num_changes; i++) {
2552 0 : actions[i].action = changes[i].action;
2553 0 : actions[i].filename = changes[i].name;
2554 : }
2555 :
2556 0 : cb_ret = state->cb(actions, num_changes, state->private_data);
2557 : }
2558 :
2559 0 : TALLOC_FREE(changes);
2560 :
2561 0 : if (cb_ret != 0) {
2562 0 : tevent_req_done(req);
2563 0 : return;
2564 : }
2565 :
2566 0 : subreq = cli_notify_send(
2567 0 : state, state->ev, state->cli, state->fnum, 1000,
2568 0 : state->completion_filter, state->recursive);
2569 0 : if (tevent_req_nomem(subreq, req)) {
2570 0 : return;
2571 : }
2572 0 : tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req);
2573 : }
2574 :
2575 0 : static void smbc_notify_cb_timedout(struct tevent_req *subreq)
2576 : {
2577 0 : struct tevent_req *req = tevent_req_callback_data(
2578 : subreq, struct tevent_req);
2579 0 : struct smbc_notify_cb_state *state = tevent_req_data(
2580 : req, struct smbc_notify_cb_state);
2581 : int cb_ret;
2582 : bool ok;
2583 :
2584 0 : ok = tevent_wakeup_recv(subreq);
2585 0 : TALLOC_FREE(subreq);
2586 0 : if (!ok) {
2587 0 : tevent_req_oom(req);
2588 0 : return;
2589 : }
2590 :
2591 0 : cb_ret = state->cb(NULL, 0, state->private_data);
2592 0 : if (cb_ret != 0) {
2593 0 : tevent_req_done(req);
2594 0 : return;
2595 : }
2596 :
2597 0 : subreq = tevent_wakeup_send(
2598 : state, state->ev,
2599 0 : tevent_timeval_current_ofs(state->callback_timeout_ms/1000,
2600 0 : state->callback_timeout_ms*1000));
2601 0 : if (tevent_req_nomem(subreq, req)) {
2602 0 : return;
2603 : }
2604 0 : tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req);
2605 : }
2606 :
2607 0 : static NTSTATUS smbc_notify_cb_recv(struct tevent_req *req)
2608 : {
2609 0 : return tevent_req_simple_recv_ntstatus(req);
2610 : }
2611 :
2612 0 : static NTSTATUS smbc_notify_cb(struct cli_state *cli, uint16_t fnum,
2613 : bool recursive, uint32_t completion_filter,
2614 : unsigned callback_timeout_ms,
2615 : smbc_notify_callback_fn cb, void *private_data)
2616 : {
2617 0 : TALLOC_CTX *frame = talloc_stackframe();
2618 : struct tevent_context *ev;
2619 : struct tevent_req *req;
2620 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
2621 :
2622 0 : ev = samba_tevent_context_init(frame);
2623 0 : if (ev == NULL) {
2624 0 : goto fail;
2625 : }
2626 0 : req = smbc_notify_cb_send(frame, ev, cli, fnum, recursive,
2627 : completion_filter,
2628 : callback_timeout_ms, cb, private_data);
2629 0 : if (req == NULL) {
2630 0 : goto fail;
2631 : }
2632 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2633 0 : goto fail;
2634 : }
2635 0 : status = smbc_notify_cb_recv(req);
2636 0 : TALLOC_FREE(req);
2637 0 : fail:
2638 0 : TALLOC_FREE(frame);
2639 0 : return status;
2640 : }
2641 :
2642 : int
2643 0 : SMBC_notify_ctx(SMBCCTX *context, SMBCFILE *dir, smbc_bool recursive,
2644 : uint32_t completion_filter, unsigned callback_timeout_ms,
2645 : smbc_notify_callback_fn cb, void *private_data)
2646 : {
2647 0 : TALLOC_CTX *frame = talloc_stackframe();
2648 : struct cli_state *cli;
2649 0 : char *server = NULL;
2650 0 : char *share = NULL;
2651 0 : char *user = NULL;
2652 0 : char *password = NULL;
2653 0 : char *options = NULL;
2654 0 : char *workgroup = NULL;
2655 0 : char *path = NULL;
2656 : uint16_t port;
2657 : NTSTATUS status;
2658 : uint16_t fnum;
2659 :
2660 0 : if ((context == NULL) || !context->internal->initialized) {
2661 0 : TALLOC_FREE(frame);
2662 0 : errno = EINVAL;
2663 0 : return -1;
2664 : }
2665 0 : if (!SMBC_dlist_contains(context->internal->files, dir)) {
2666 0 : TALLOC_FREE(frame);
2667 0 : errno = EBADF;
2668 0 : return -1;
2669 : }
2670 :
2671 0 : if (SMBC_parse_path(frame,
2672 : context,
2673 0 : dir->fname,
2674 : &workgroup,
2675 : &server,
2676 : &port,
2677 : &share,
2678 : &path,
2679 : &user,
2680 : &password,
2681 : &options)) {
2682 0 : DEBUG(4, ("no valid path\n"));
2683 0 : TALLOC_FREE(frame);
2684 0 : errno = EINVAL + 8194;
2685 0 : return -1;
2686 : }
2687 :
2688 0 : DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2689 : "path='%s' options='%s'\n",
2690 : dir->fname, server, share, path, options));
2691 :
2692 0 : DEBUG(4, ("%s(%p, %d, %"PRIu32")\n", __func__, dir,
2693 : (int)recursive, completion_filter));
2694 :
2695 0 : cli = dir->srv->cli;
2696 0 : status = cli_ntcreate(
2697 : cli, path, 0, FILE_READ_DATA, 0,
2698 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2699 : FILE_OPEN, 0, 0, &fnum, NULL);
2700 0 : if (!NT_STATUS_IS_OK(status)) {
2701 0 : TALLOC_FREE(frame);
2702 0 : errno = cli_status_to_errno(status);
2703 0 : return -1;
2704 : }
2705 :
2706 0 : status = smbc_notify_cb(cli, fnum, recursive != 0, completion_filter,
2707 : callback_timeout_ms, cb, private_data);
2708 0 : if (!NT_STATUS_IS_OK(status)) {
2709 0 : cli_close(cli, fnum);
2710 0 : TALLOC_FREE(frame);
2711 0 : errno = cli_status_to_errno(status);
2712 0 : return -1;
2713 : }
2714 :
2715 0 : cli_close(cli, fnum);
2716 :
2717 0 : TALLOC_FREE(frame);
2718 0 : return 0;
2719 : }
|