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