s3:idmap_ad: add support for ADS_AUTH_SASL_{STARTTLS,LDAPS}
[Samba.git] / source3 / libsmb / libsmb_dir.c
blob34d6731b4219609602f0badb248f08b1f1621333
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
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.
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.
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/>.
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"
40 * Routine to open a directory
41 * We accept the URL syntax explained in SMBC_parse_path(), above.
44 static void remove_dirplus(SMBCFILE *dir)
46 struct smbc_dirplus_list *d = NULL;
48 d = dir->dirplus_list;
49 while (d != NULL) {
50 struct smbc_dirplus_list *f = d;
51 d = d->next;
53 SAFE_FREE(f->smb_finfo->short_name);
54 SAFE_FREE(f->smb_finfo->name);
55 SAFE_FREE(f->smb_finfo);
56 SAFE_FREE(f);
59 dir->dirplus_list = NULL;
60 dir->dirplus_end = NULL;
61 dir->dirplus_next = NULL;
64 static void
65 remove_dir(SMBCFILE *dir)
67 struct smbc_dir_list *d,*f;
69 d = dir->dir_list;
70 while (d) {
72 f = d; d = d->next;
74 SAFE_FREE(f->dirent);
75 SAFE_FREE(f);
79 dir->dir_list = dir->dir_end = dir->dir_next = NULL;
83 static int
84 add_dirent(SMBCFILE *dir,
85 const char *name,
86 const char *comment,
87 uint32_t type)
89 struct smbc_dirent *dirent;
90 int size;
91 int name_length = (name == NULL ? 0 : strlen(name));
92 int comment_len = (comment == NULL ? 0 : strlen(comment));
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.
99 size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
101 dirent = (struct smbc_dirent *)SMB_MALLOC(size);
103 if (!dirent) {
105 dir->dir_error = ENOMEM;
106 return -1;
110 ZERO_STRUCTP(dirent);
112 if (dir->dir_list == NULL) {
114 dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
115 if (!dir->dir_list) {
117 SAFE_FREE(dirent);
118 dir->dir_error = ENOMEM;
119 return -1;
122 ZERO_STRUCTP(dir->dir_list);
124 dir->dir_end = dir->dir_next = dir->dir_list;
126 else {
128 dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
130 if (!dir->dir_end->next) {
132 SAFE_FREE(dirent);
133 dir->dir_error = ENOMEM;
134 return -1;
137 ZERO_STRUCTP(dir->dir_end->next);
139 dir->dir_end = dir->dir_end->next;
142 dir->dir_end->next = NULL;
143 dir->dir_end->dirent = dirent;
145 dirent->smbc_type = type;
146 dirent->namelen = name_length;
147 dirent->commentlen = comment_len;
148 dirent->dirlen = size;
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.
155 strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
156 dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
157 strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
159 return 0;
163 static int add_dirplus(SMBCFILE *dir, struct file_info *finfo)
165 struct smbc_dirplus_list *new_entry = NULL;
166 struct libsmb_file_info *info = NULL;
168 new_entry = SMB_MALLOC_P(struct smbc_dirplus_list);
169 if (new_entry == NULL) {
170 dir->dir_error = ENOMEM;
171 return -1;
173 ZERO_STRUCTP(new_entry);
174 new_entry->ino = finfo->ino;
176 info = SMB_MALLOC_P(struct libsmb_file_info);
177 if (info == NULL) {
178 SAFE_FREE(new_entry);
179 dir->dir_error = ENOMEM;
180 return -1;
183 ZERO_STRUCTP(info);
185 info->btime_ts = finfo->btime_ts;
186 info->atime_ts = finfo->atime_ts;
187 info->ctime_ts = finfo->ctime_ts;
188 info->mtime_ts = finfo->mtime_ts;
189 info->gid = finfo->gid;
190 info->attrs = finfo->attr;
191 info->size = finfo->size;
192 info->uid = finfo->uid;
193 info->name = SMB_STRDUP(finfo->name);
194 if (info->name == NULL) {
195 SAFE_FREE(info);
196 SAFE_FREE(new_entry);
197 dir->dir_error = ENOMEM;
198 return -1;
201 if (finfo->short_name) {
202 info->short_name = SMB_STRDUP(finfo->short_name);
203 } else {
204 info->short_name = SMB_STRDUP("");
207 if (info->short_name == NULL) {
208 SAFE_FREE(info->name);
209 SAFE_FREE(info);
210 SAFE_FREE(new_entry);
211 dir->dir_error = ENOMEM;
212 return -1;
214 new_entry->smb_finfo = info;
216 /* Now add to the list. */
217 if (dir->dirplus_list == NULL) {
218 /* Empty list - point everything at new_entry. */
219 dir->dirplus_list = new_entry;
220 dir->dirplus_end = new_entry;
221 dir->dirplus_next = new_entry;
222 } else {
223 /* Append to list but leave the ->next cursor alone. */
224 dir->dirplus_end->next = new_entry;
225 dir->dirplus_end = new_entry;
228 return 0;
231 static void
232 list_unique_wg_fn(const char *name,
233 uint32_t type,
234 const char *comment,
235 void *state)
237 SMBCFILE *dir = (SMBCFILE *)state;
238 struct smbc_dir_list *dir_list;
239 struct smbc_dirent *dirent;
240 int dirent_type;
241 int do_remove = 0;
243 dirent_type = dir->dir_type;
245 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. */
252 /* Point to the one just added */
253 dirent = dir->dir_end->dirent;
255 /* See if this was a duplicate */
256 for (dir_list = dir->dir_list;
257 dir_list != dir->dir_end;
258 dir_list = dir_list->next) {
259 if (! do_remove &&
260 strcmp(dir_list->dirent->name, dirent->name) == 0) {
261 /* Duplicate. End end of list need to be removed. */
262 do_remove = 1;
265 if (do_remove && dir_list->next == dir->dir_end) {
266 /* Found the end of the list. Remove it. */
267 dir->dir_end = dir_list;
268 free(dir_list->next);
269 free(dirent);
270 dir_list->next = NULL;
271 break;
276 static void
277 list_fn(const char *name,
278 uint32_t type,
279 const char *comment,
280 void *state)
282 SMBCFILE *dir = (SMBCFILE *)state;
283 int dirent_type;
286 * We need to process the type a little ...
288 * Disk share = 0x00000000
289 * Print share = 0x00000001
290 * Comms share = 0x00000002 (obsolete?)
291 * IPC$ share = 0x00000003
293 * administrative shares:
294 * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
297 if (dir->dir_type == SMBC_FILE_SHARE) {
298 switch (type) {
299 case 0 | 0x80000000:
300 case 0:
301 dirent_type = SMBC_FILE_SHARE;
302 break;
304 case 1:
305 dirent_type = SMBC_PRINTER_SHARE;
306 break;
308 case 2:
309 dirent_type = SMBC_COMMS_SHARE;
310 break;
312 case 3 | 0x80000000:
313 case 3:
314 dirent_type = SMBC_IPC_SHARE;
315 break;
317 default:
318 dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
319 break;
322 else {
323 dirent_type = dir->dir_type;
326 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. */
334 static NTSTATUS
335 dir_list_fn(struct file_info *finfo,
336 const char *mask,
337 void *state)
339 SMBCFILE *dirp = (SMBCFILE *)state;
340 int ret;
342 if (add_dirent((SMBCFILE *)state, finfo->name, "",
343 (finfo->attr&FILE_ATTRIBUTE_DIRECTORY?SMBC_DIR:SMBC_FILE)) < 0) {
344 SMBCFILE *dir = (SMBCFILE *)state;
345 return map_nt_error_from_unix(dir->dir_error);
347 ret = add_dirplus(dirp, finfo);
348 if (ret < 0) {
349 return map_nt_error_from_unix(dirp->dir_error);
351 return NT_STATUS_OK;
354 static NTSTATUS
355 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)
362 uint32_t i;
363 WERROR result;
364 uint32_t preferred_len = 0xffffffff;
365 uint32_t type;
366 struct srvsvc_NetShareInfoCtr info_ctr;
367 struct srvsvc_NetShareCtr1 ctr1;
368 fstring name = "";
369 fstring comment = "";
370 struct rpc_pipe_client *pipe_hnd = NULL;
371 NTSTATUS nt_status;
372 uint32_t resume_handle = 0;
373 uint32_t total_entries = 0;
374 struct dcerpc_binding_handle *b;
376 /* Open the server service pipe */
377 nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc,
378 &pipe_hnd);
379 if (!NT_STATUS_IS_OK(nt_status)) {
380 DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
381 goto done;
384 ZERO_STRUCT(info_ctr);
385 ZERO_STRUCT(ctr1);
387 info_ctr.level = 1;
388 info_ctr.ctr.ctr1 = &ctr1;
390 b = pipe_hnd->binding_handle;
392 /* Issue the NetShareEnum RPC call and retrieve the response */
393 nt_status = dcerpc_srvsvc_NetShareEnumAll(b, talloc_tos(),
394 pipe_hnd->desthost,
395 &info_ctr,
396 preferred_len,
397 &total_entries,
398 &resume_handle,
399 &result);
401 /* Was it successful? */
402 if (!NT_STATUS_IS_OK(nt_status)) {
403 /* Nope. Go clean up. */
404 goto done;
407 if (!W_ERROR_IS_OK(result)) {
408 /* Nope. Go clean up. */
409 nt_status = werror_to_ntstatus(result);
410 goto done;
413 if (total_entries == 0) {
414 /* Nope. Go clean up. */
415 nt_status = NT_STATUS_NOT_FOUND;
416 goto done;
419 /* For each returned entry... */
420 for (i = 0; i < info_ctr.ctr.ctr1->count; i++) {
422 /* pull out the share name */
423 fstrcpy(name, info_ctr.ctr.ctr1->array[i].name);
425 /* pull out the share's comment */
426 fstrcpy(comment, info_ctr.ctr.ctr1->array[i].comment);
428 /* Get the type value */
429 type = info_ctr.ctr.ctr1->array[i].type;
431 /* Add this share to the list */
432 (*fn)(name, type, comment, state);
435 done:
436 /* Close the server service pipe */
437 TALLOC_FREE(pipe_hnd);
439 /* Tell 'em if it worked */
440 return nt_status;
445 * Verify that the options specified in a URL are valid
448 SMBC_check_options(char *server,
449 char *share,
450 char *path,
451 char *options)
453 DEBUG(4, ("SMBC_check_options(): server='%s' share='%s' "
454 "path='%s' options='%s'\n",
455 server, share, path, options));
457 /* No options at all is always ok */
458 if (! *options) return 0;
460 /* Currently, we don't support any options. */
461 return -1;
465 SMBCFILE *
466 SMBC_opendir_ctx(SMBCCTX *context,
467 const char *fname)
469 char *server = NULL;
470 char *share = NULL;
471 char *user = NULL;
472 char *password = NULL;
473 char *options = NULL;
474 char *workgroup = NULL;
475 char *path = NULL;
476 size_t path_len = 0;
477 uint16_t port = 0;
478 SMBCSRV *srv = NULL;
479 SMBCFILE *dir = NULL;
480 struct sockaddr_storage rem_ss;
481 TALLOC_CTX *frame = talloc_stackframe();
483 if (!context || !context->internal->initialized) {
484 DEBUG(4, ("no valid context\n"));
485 TALLOC_FREE(frame);
486 errno = EINVAL + 8192;
487 return NULL;
491 if (!fname) {
492 DEBUG(4, ("no valid fname\n"));
493 TALLOC_FREE(frame);
494 errno = EINVAL + 8193;
495 return NULL;
498 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 DEBUG(4, ("no valid path\n"));
510 TALLOC_FREE(frame);
511 errno = EINVAL + 8194;
512 return NULL;
515 DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
516 "path='%s' options='%s'\n",
517 fname, server, share, path, options));
519 /* Ensure the options are valid */
520 if (SMBC_check_options(server, share, path, options)) {
521 DEBUG(4, ("unacceptable options (%s)\n", options));
522 TALLOC_FREE(frame);
523 errno = EINVAL + 8195;
524 return NULL;
527 if (!user || user[0] == (char)0) {
528 user = talloc_strdup(frame, smbc_getUser(context));
529 if (!user) {
530 TALLOC_FREE(frame);
531 errno = ENOMEM;
532 return NULL;
536 dir = SMB_MALLOC_P(SMBCFILE);
538 if (!dir) {
539 TALLOC_FREE(frame);
540 errno = ENOMEM;
541 return NULL;
544 ZERO_STRUCTP(dir);
546 dir->cli_fd = 0;
547 dir->fname = SMB_STRDUP(fname);
548 if (dir->fname == NULL) {
549 SAFE_FREE(dir);
550 TALLOC_FREE(frame);
551 errno = ENOMEM;
552 return NULL;
554 dir->srv = NULL;
555 dir->offset = 0;
556 dir->file = False;
557 dir->dir_list = dir->dir_next = dir->dir_end = NULL;
559 if (server[0] == (char)0) {
561 size_t i;
562 size_t count = 0;
563 size_t max_lmb_count;
564 struct sockaddr_storage *ip_list;
565 struct sockaddr_storage server_addr;
566 struct cli_credentials *creds = NULL;
567 NTSTATUS status;
569 if (share[0] != (char)0 || path[0] != (char)0) {
571 if (dir) {
572 SAFE_FREE(dir->fname);
573 SAFE_FREE(dir);
575 TALLOC_FREE(frame);
576 errno = EINVAL + 8196;
577 return NULL;
580 /* Determine how many local master browsers to query */
581 max_lmb_count = (smbc_getOptionBrowseMaxLmbCount(context) == 0
582 ? INT_MAX
583 : smbc_getOptionBrowseMaxLmbCount(context));
585 creds = cli_credentials_init(frame);
586 if (creds == NULL) {
587 if (dir) {
588 SAFE_FREE(dir->fname);
589 SAFE_FREE(dir);
591 TALLOC_FREE(frame);
592 errno = ENOMEM;
593 return NULL;
596 (void)cli_credentials_set_username(creds, user, CRED_SPECIFIED);
597 (void)cli_credentials_set_password(creds, password, CRED_SPECIFIED);
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.
608 ip_list = NULL;
609 status = name_resolve_bcast(talloc_tos(),
610 MSBROWSE,
612 &ip_list,
613 &count);
614 if (!NT_STATUS_IS_OK(status))
617 TALLOC_FREE(ip_list);
619 if (!find_master_ip(workgroup, &server_addr)) {
621 if (dir) {
622 SAFE_FREE(dir->fname);
623 SAFE_FREE(dir);
625 TALLOC_FREE(frame);
626 errno = ENOENT;
627 return NULL;
630 ip_list = (struct sockaddr_storage *)talloc_memdup(
631 talloc_tos(), &server_addr,
632 sizeof(server_addr));
633 if (ip_list == NULL) {
634 if (dir) {
635 SAFE_FREE(dir->fname);
636 SAFE_FREE(dir);
638 TALLOC_FREE(frame);
639 errno = ENOMEM;
640 return NULL;
642 count = 1;
645 for (i = 0; i < count && i < max_lmb_count; i++) {
646 char addr[INET6_ADDRSTRLEN];
647 char *wg_ptr = NULL;
648 struct cli_state *cli = NULL;
650 print_sockaddr(addr, sizeof(addr), &ip_list[i]);
651 DEBUG(99, ("Found master browser %zu of %zu: %s\n",
652 i+1, MAX(count, max_lmb_count),
653 addr));
655 cli = get_ipc_connect_master_ip(talloc_tos(),
656 &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 if (!cli) {
662 continue;
665 workgroup = talloc_strdup(frame, wg_ptr);
666 server = talloc_strdup(frame, smbXcli_conn_remote_name(cli->conn));
668 cli_shutdown(cli);
670 if (!workgroup || !server) {
671 if (dir) {
672 SAFE_FREE(dir->fname);
673 SAFE_FREE(dir);
675 TALLOC_FREE(frame);
676 errno = ENOMEM;
677 return NULL;
680 DEBUG(4, ("using workgroup %s %s\n",
681 workgroup, server));
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.
690 srv = SMBC_server(frame, context, True, server, port, "IPC$",
691 &workgroup, &user, &password);
692 if (!srv) {
693 continue;
696 if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) {
697 continue;
700 dir->srv = srv;
701 dir->dir_type = SMBC_WORKGROUP;
703 /* Now, list the stuff ... */
705 if (!cli_NetServerEnum(srv->cli,
706 workgroup,
707 SV_TYPE_DOMAIN_ENUM,
708 list_unique_wg_fn,
709 (void *)dir)) {
710 continue;
714 TALLOC_FREE(ip_list);
715 } else {
717 * Server not an empty string ... Check the rest and see what
718 * gives
720 if (*share == '\0') {
721 if (*path != '\0') {
723 /* Should not have empty share with path */
724 if (dir) {
725 SAFE_FREE(dir->fname);
726 SAFE_FREE(dir);
728 TALLOC_FREE(frame);
729 errno = EINVAL + 8197;
730 return NULL;
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.
744 * See if we have an existing server. Do not
745 * establish a connection if one does not already
746 * exist.
748 srv = SMBC_server(frame, context, False,
749 server, port, "IPC$",
750 &workgroup, &user, &password);
753 * If no existing server and not an IP addr, look for
754 * LMB or DMB
756 if (!srv &&
757 !is_ipaddress(server) &&
758 (resolve_name(server, &rem_ss, 0x1d, false) || /* LMB */
759 resolve_name(server, &rem_ss, 0x1b, false) )) { /* DMB */
761 * "server" is actually a workgroup name,
762 * not a server. Make this clear.
764 char *wgroup = server;
765 fstring buserver;
767 dir->dir_type = SMBC_SERVER;
770 * Get the backup list ...
772 if (!name_status_find(wgroup, 0, 0,
773 &rem_ss, buserver)) {
774 char addr[INET6_ADDRSTRLEN];
776 print_sockaddr(addr, sizeof(addr), &rem_ss);
777 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 if (dir) {
784 SAFE_FREE(dir->fname);
785 SAFE_FREE(dir);
787 TALLOC_FREE(frame);
788 errno = EPERM;
789 return NULL;
794 * Get a connection to IPC$ on the server if
795 * we do not already have one
797 srv = SMBC_server(frame, context, True,
798 buserver, port, "IPC$",
799 &workgroup,
800 &user, &password);
801 if (!srv) {
802 DEBUG(0, ("got no contact to IPC$\n"));
803 if (dir) {
804 SAFE_FREE(dir->fname);
805 SAFE_FREE(dir);
807 TALLOC_FREE(frame);
808 return NULL;
812 dir->srv = srv;
814 if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) {
815 if (dir) {
816 SAFE_FREE(dir->fname);
817 SAFE_FREE(dir);
819 TALLOC_FREE(frame);
820 return NULL;
823 /* Now, list the servers ... */
824 if (!cli_NetServerEnum(srv->cli, wgroup,
825 0x0000FFFE, list_fn,
826 (void *)dir)) {
828 if (dir) {
829 SAFE_FREE(dir->fname);
830 SAFE_FREE(dir);
832 TALLOC_FREE(frame);
833 return NULL;
835 } else if (srv ||
836 (resolve_name(server, &rem_ss, 0x20, false))) {
837 NTSTATUS status;
840 * If we hadn't found the server, get one now
842 if (!srv) {
843 srv = SMBC_server(frame, context, True,
844 server, port, "IPC$",
845 &workgroup,
846 &user, &password);
849 if (!srv) {
850 if (dir) {
851 SAFE_FREE(dir->fname);
852 SAFE_FREE(dir);
854 TALLOC_FREE(frame);
855 return NULL;
859 dir->dir_type = SMBC_FILE_SHARE;
860 dir->srv = srv;
862 /* List the shares ... */
864 status = net_share_enum_rpc(srv->cli,
865 list_fn,
866 (void *)dir);
867 if (!NT_STATUS_IS_OK(status) &&
868 smbXcli_conn_protocol(srv->cli->conn) <=
869 PROTOCOL_NT1) {
871 * Only call cli_RNetShareEnum()
872 * on SMB1 connections, not SMB2+.
874 int rc = cli_RNetShareEnum(srv->cli,
875 list_fn,
876 (void *)dir);
877 if (rc != 0) {
878 status = cli_nt_error(srv->cli);
879 } else {
880 status = NT_STATUS_OK;
883 if (!NT_STATUS_IS_OK(status)) {
885 * Set cli->raw_status so SMBC_errno()
886 * will correctly return the error.
888 srv->cli->raw_status = status;
889 if (dir != NULL) {
890 SAFE_FREE(dir->fname);
891 SAFE_FREE(dir);
893 TALLOC_FREE(frame);
894 errno = map_errno_from_nt_status(
895 status);
896 return NULL;
898 } else {
899 /* Neither the workgroup nor server exists */
900 errno = ECONNREFUSED;
901 if (dir) {
902 SAFE_FREE(dir->fname);
903 SAFE_FREE(dir);
905 TALLOC_FREE(frame);
906 return NULL;
910 else {
912 * The server and share are specified ... work from
913 * there ...
915 char *targetpath;
916 struct cli_state *targetcli;
917 struct cli_credentials *creds = NULL;
918 NTSTATUS status;
920 /* We connect to the server and list the directory */
921 dir->dir_type = SMBC_FILE_SHARE;
923 srv = SMBC_server(frame, context, True, server, port, share,
924 &workgroup, &user, &password);
926 if (!srv) {
927 if (dir) {
928 SAFE_FREE(dir->fname);
929 SAFE_FREE(dir);
931 TALLOC_FREE(frame);
932 return NULL;
935 dir->srv = srv;
937 /* Now, list the files ... */
939 path_len = strlen(path);
940 path = talloc_asprintf_append(path, "\\*");
941 if (!path) {
942 if (dir) {
943 SAFE_FREE(dir->fname);
944 SAFE_FREE(dir);
946 TALLOC_FREE(frame);
947 return NULL;
950 creds = context->internal->creds;
952 status = cli_resolve_path(
953 frame, "",
954 creds,
955 srv->cli, path, &targetcli, &targetpath);
956 if (!NT_STATUS_IS_OK(status)) {
957 d_printf("Could not resolve %s\n", path);
958 if (dir) {
959 SAFE_FREE(dir->fname);
960 SAFE_FREE(dir);
962 TALLOC_FREE(frame);
963 return NULL;
966 status = cli_list(targetcli, targetpath,
967 FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
968 dir_list_fn, (void *)dir);
969 if (!NT_STATUS_IS_OK(status)) {
970 int saved_errno;
971 if (dir) {
972 SAFE_FREE(dir->fname);
973 SAFE_FREE(dir);
975 saved_errno = cli_status_to_errno(status);
977 if (saved_errno == EINVAL) {
978 struct stat sb = {0};
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.
986 path[path_len] = '\0'; /* restore original path */
988 status = SMBC_getatr(
989 context,
990 srv,
991 path,
992 &sb);
993 if (NT_STATUS_IS_OK(status) &&
994 !S_ISDIR(sb.st_mode)) {
996 /* It is. Correct the error value */
997 saved_errno = ENOTDIR;
1002 * If there was an error and the server is no
1003 * good any more...
1005 if (cli_is_error(targetcli) &&
1006 smbc_getFunctionCheckServer(context)(context, srv)) {
1008 /* ... then remove it. */
1009 if (smbc_getFunctionRemoveUnusedServer(context)(context,
1010 srv)) {
1012 * We could not remove the
1013 * server completely, remove
1014 * it from the cache so we
1015 * will not get it again. It
1016 * will be removed when the
1017 * last file/dir is closed.
1019 smbc_getFunctionRemoveCachedServer(context)(context, srv);
1023 TALLOC_FREE(frame);
1024 errno = saved_errno;
1025 return NULL;
1031 DLIST_ADD(context->internal->files, dir);
1032 TALLOC_FREE(frame);
1033 return dir;
1038 * Routine to close a directory
1042 SMBC_closedir_ctx(SMBCCTX *context,
1043 SMBCFILE *dir)
1045 TALLOC_CTX *frame = NULL;
1047 if (!context || !context->internal->initialized) {
1048 errno = EINVAL;
1049 return -1;
1052 if (dir == NULL) {
1053 return 0;
1056 frame = talloc_stackframe();
1058 if (!SMBC_dlist_contains(context->internal->files, dir)) {
1059 errno = EBADF;
1060 TALLOC_FREE(frame);
1061 return -1;
1064 remove_dir(dir); /* Clean it up */
1065 remove_dirplus(dir);
1067 DLIST_REMOVE(context->internal->files, dir);
1069 SAFE_FREE(dir->fname);
1070 SAFE_FREE(dir); /* Free the space too */
1072 TALLOC_FREE(frame);
1073 return 0;
1077 static int
1078 smbc_readdir_internal(SMBCCTX * context,
1079 struct smbc_dirent *dest,
1080 struct smbc_dirent *src,
1081 int max_namebuf_len)
1083 if (smbc_getOptionUrlEncodeReaddirEntries(context)) {
1084 int remaining_len;
1086 /* url-encode the name. get back remaining buffer space */
1087 remaining_len =
1088 smbc_urlencode(dest->name, src->name, max_namebuf_len);
1090 /* -1 means no null termination. */
1091 if (remaining_len < 0) {
1092 return -1;
1095 /* We now know the name length */
1096 dest->namelen = strlen(dest->name);
1098 if (dest->namelen + 1 < 1) {
1099 /* Integer wrap. */
1100 return -1;
1103 if (dest->namelen + 1 >= max_namebuf_len) {
1104 /* Out of space for comment. */
1105 return -1;
1108 /* Save the pointer to the beginning of the comment */
1109 dest->comment = dest->name + dest->namelen + 1;
1111 if (remaining_len < 1) {
1112 /* No room for comment null termination. */
1113 return -1;
1116 /* Copy the comment */
1117 strlcpy(dest->comment, src->comment, remaining_len);
1119 /* Save other fields */
1120 dest->smbc_type = src->smbc_type;
1121 dest->commentlen = strlen(dest->comment);
1122 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
1123 (char *) dest);
1124 } else {
1126 /* No encoding. Just copy the entry as is. */
1127 if (src->dirlen > max_namebuf_len) {
1128 return -1;
1130 memcpy(dest, src, src->dirlen);
1131 if (src->namelen + 1 < 1) {
1132 /* Integer wrap */
1133 return -1;
1135 if (src->namelen + 1 >= max_namebuf_len) {
1136 /* Comment off the end. */
1137 return -1;
1139 dest->comment = (char *)(&dest->name + src->namelen + 1);
1141 return 0;
1145 * Routine to get a directory entry
1148 struct smbc_dirent *
1149 SMBC_readdir_ctx(SMBCCTX *context,
1150 SMBCFILE *dir)
1152 int maxlen;
1153 int ret;
1154 struct smbc_dirent *dirp, *dirent;
1155 TALLOC_CTX *frame = talloc_stackframe();
1157 /* Check that all is ok first ... */
1159 if (!context || !context->internal->initialized) {
1161 errno = EINVAL;
1162 DEBUG(0, ("Invalid context in SMBC_readdir_ctx()\n"));
1163 TALLOC_FREE(frame);
1164 return NULL;
1168 if (!SMBC_dlist_contains(context->internal->files, dir)) {
1170 errno = EBADF;
1171 DEBUG(0, ("Invalid dir in SMBC_readdir_ctx()\n"));
1172 TALLOC_FREE(frame);
1173 return NULL;
1177 if (dir->file != False) { /* FIXME, should be dir, perhaps */
1179 errno = ENOTDIR;
1180 DEBUG(0, ("Found file vs directory in SMBC_readdir_ctx()\n"));
1181 TALLOC_FREE(frame);
1182 return NULL;
1186 if (!dir->dir_next) {
1187 TALLOC_FREE(frame);
1188 return NULL;
1191 dirent = dir->dir_next->dirent;
1192 if (!dirent) {
1194 errno = ENOENT;
1195 TALLOC_FREE(frame);
1196 return NULL;
1200 dirp = &context->internal->dirent;
1201 maxlen = sizeof(context->internal->_dirent_name);
1203 ret = smbc_readdir_internal(context, dirp, dirent, maxlen);
1204 if (ret == -1) {
1205 errno = EINVAL;
1206 TALLOC_FREE(frame);
1207 return NULL;
1210 dir->dir_next = dir->dir_next->next;
1213 * If we are returning file entries, we
1214 * have a duplicate list in dirplus.
1216 * Update dirplus_next also so readdir and
1217 * readdirplus are kept in sync.
1219 if (dir->dirplus_list != NULL) {
1220 dir->dirplus_next = dir->dirplus_next->next;
1223 TALLOC_FREE(frame);
1224 return dirp;
1228 * Routine to get a directory entry with all attributes
1231 const struct libsmb_file_info *
1232 SMBC_readdirplus_ctx(SMBCCTX *context,
1233 SMBCFILE *dir)
1235 struct libsmb_file_info *smb_finfo = NULL;
1236 TALLOC_CTX *frame = talloc_stackframe();
1238 /* Check that all is ok first ... */
1240 if (context == NULL || !context->internal->initialized) {
1241 DBG_ERR("Invalid context in SMBC_readdirplus_ctx()\n");
1242 TALLOC_FREE(frame);
1243 errno = EINVAL;
1244 return NULL;
1247 if (!SMBC_dlist_contains(context->internal->files, dir)) {
1248 DBG_ERR("Invalid dir in SMBC_readdirplus_ctx()\n");
1249 TALLOC_FREE(frame);
1250 errno = EBADF;
1251 return NULL;
1254 if (dir->dirplus_next == NULL) {
1255 TALLOC_FREE(frame);
1256 return NULL;
1259 smb_finfo = dir->dirplus_next->smb_finfo;
1260 if (smb_finfo == NULL) {
1261 TALLOC_FREE(frame);
1262 errno = ENOENT;
1263 return NULL;
1265 dir->dirplus_next = dir->dirplus_next->next;
1268 * If we are returning file entries, we
1269 * have a duplicate list in dir_list
1271 * Update dir_next also so readdir and
1272 * readdirplus are kept in sync.
1274 if (dir->dir_list) {
1275 dir->dir_next = dir->dir_next->next;
1278 TALLOC_FREE(frame);
1279 return smb_finfo;
1283 * Routine to get a directory entry plus a filled in stat structure if
1284 * requested.
1287 const struct libsmb_file_info *SMBC_readdirplus2_ctx(SMBCCTX *context,
1288 SMBCFILE *dir,
1289 struct stat *st)
1291 struct libsmb_file_info *smb_finfo = NULL;
1292 struct smbc_dirplus_list *dp_list = NULL;
1293 ino_t ino;
1294 char *full_pathname = NULL;
1295 char *workgroup = NULL;
1296 char *server = NULL;
1297 uint16_t port = 0;
1298 char *share = NULL;
1299 char *path = NULL;
1300 char *user = NULL;
1301 char *password = NULL;
1302 char *options = NULL;
1303 int rc;
1304 TALLOC_CTX *frame = NULL;
1307 * Allow caller to pass in NULL for stat pointer if
1308 * required. This makes this call identical to
1309 * smbc_readdirplus().
1312 if (st == NULL) {
1313 return SMBC_readdirplus_ctx(context, dir);
1316 frame = talloc_stackframe();
1318 /* Check that all is ok first ... */
1319 if (context == NULL || !context->internal->initialized) {
1320 DBG_ERR("Invalid context in SMBC_readdirplus2_ctx()\n");
1321 TALLOC_FREE(frame);
1322 errno = EINVAL;
1323 return NULL;
1326 if (!SMBC_dlist_contains(context->internal->files, dir)) {
1327 DBG_ERR("Invalid dir in SMBC_readdirplus2_ctx()\n");
1328 TALLOC_FREE(frame);
1329 errno = EBADF;
1330 return NULL;
1333 dp_list = dir->dirplus_next;
1334 if (dp_list == NULL) {
1335 TALLOC_FREE(frame);
1336 return NULL;
1339 ino = (ino_t)dp_list->ino;
1341 smb_finfo = dp_list->smb_finfo;
1342 if (smb_finfo == NULL) {
1343 TALLOC_FREE(frame);
1344 errno = ENOENT;
1345 return NULL;
1348 full_pathname = talloc_asprintf(frame,
1349 "%s/%s",
1350 dir->fname,
1351 smb_finfo->name);
1352 if (full_pathname == NULL) {
1353 TALLOC_FREE(frame);
1354 errno = ENOENT;
1355 return NULL;
1358 rc = SMBC_parse_path(frame,
1359 context,
1360 full_pathname,
1361 &workgroup,
1362 &server,
1363 &port,
1364 &share,
1365 &path,
1366 &user,
1367 &password,
1368 &options);
1369 if (rc != 0) {
1370 TALLOC_FREE(frame);
1371 errno = ENOENT;
1372 return NULL;
1375 setup_stat(st,
1376 path,
1377 smb_finfo->size,
1378 smb_finfo->attrs,
1379 ino,
1380 dir->srv->dev,
1381 smb_finfo->atime_ts,
1382 smb_finfo->ctime_ts,
1383 smb_finfo->mtime_ts);
1385 TALLOC_FREE(full_pathname);
1387 dir->dirplus_next = dir->dirplus_next->next;
1390 * If we are returning file entries, we
1391 * have a duplicate list in dir_list
1393 * Update dir_next also so readdir and
1394 * readdirplus are kept in sync.
1396 if (dir->dir_list) {
1397 dir->dir_next = dir->dir_next->next;
1400 TALLOC_FREE(frame);
1401 return smb_finfo;
1405 * Routine to get directory entries
1409 SMBC_getdents_ctx(SMBCCTX *context,
1410 SMBCFILE *dir,
1411 struct smbc_dirent *dirp,
1412 int count)
1414 int rem = count;
1415 int reqd;
1416 int maxlen;
1417 char *ndir = (char *)dirp;
1418 struct smbc_dir_list *dirlist;
1419 TALLOC_CTX *frame = talloc_stackframe();
1421 /* Check that all is ok first ... */
1423 if (!context || !context->internal->initialized) {
1425 errno = EINVAL;
1426 TALLOC_FREE(frame);
1427 return -1;
1431 if (!SMBC_dlist_contains(context->internal->files, dir)) {
1433 errno = EBADF;
1434 TALLOC_FREE(frame);
1435 return -1;
1439 if (dir->file != False) { /* FIXME, should be dir, perhaps */
1441 errno = ENOTDIR;
1442 TALLOC_FREE(frame);
1443 return -1;
1448 * Now, retrieve the number of entries that will fit in what was passed
1449 * We have to figure out if the info is in the list, or we need to
1450 * send a request to the server to get the info.
1453 while ((dirlist = dir->dir_next)) {
1454 int ret;
1455 struct smbc_dirent *dirent;
1456 struct smbc_dirent *currentEntry = (struct smbc_dirent *)ndir;
1458 if (!dirlist->dirent) {
1460 errno = ENOENT; /* Bad error */
1461 TALLOC_FREE(frame);
1462 return -1;
1466 /* Do urlencoding of next entry, if so selected */
1467 dirent = &context->internal->dirent;
1468 maxlen = sizeof(context->internal->_dirent_name);
1469 ret = smbc_readdir_internal(context, dirent,
1470 dirlist->dirent, maxlen);
1471 if (ret == -1) {
1472 errno = EINVAL;
1473 TALLOC_FREE(frame);
1474 return -1;
1477 reqd = dirent->dirlen;
1479 if (rem < reqd) {
1481 if (rem < count) { /* We managed to copy something */
1483 errno = 0;
1484 TALLOC_FREE(frame);
1485 return count - rem;
1488 else { /* Nothing copied ... */
1490 errno = EINVAL; /* Not enough space ... */
1491 TALLOC_FREE(frame);
1492 return -1;
1498 memcpy(currentEntry, dirent, reqd); /* Copy the data in ... */
1500 currentEntry->comment = &currentEntry->name[0] +
1501 dirent->namelen + 1;
1503 ndir += reqd;
1504 rem -= reqd;
1506 /* Try and align the struct for the next entry
1507 on a valid pointer boundary by appending zeros */
1508 while((rem > 0) && ((uintptr_t)ndir & (sizeof(void*) - 1))) {
1509 *ndir = '\0';
1510 rem--;
1511 ndir++;
1512 currentEntry->dirlen++;
1515 dir->dir_next = dirlist = dirlist -> next;
1518 * If we are returning file entries, we
1519 * have a duplicate list in dirplus.
1521 * Update dirplus_next also so readdir and
1522 * readdirplus are kept in sync.
1524 if (dir->dirplus_list != NULL) {
1525 dir->dirplus_next = dir->dirplus_next->next;
1529 TALLOC_FREE(frame);
1531 if (rem == count)
1532 return 0;
1533 else
1534 return count - rem;
1539 * Routine to create a directory ...
1543 SMBC_mkdir_ctx(SMBCCTX *context,
1544 const char *fname,
1545 mode_t mode)
1547 SMBCSRV *srv = NULL;
1548 char *server = NULL;
1549 char *share = NULL;
1550 char *user = NULL;
1551 char *password = NULL;
1552 char *workgroup = NULL;
1553 char *path = NULL;
1554 char *targetpath = NULL;
1555 uint16_t port = 0;
1556 struct cli_state *targetcli = NULL;
1557 struct cli_credentials *creds = NULL;
1558 TALLOC_CTX *frame = talloc_stackframe();
1559 NTSTATUS status;
1561 if (!context || !context->internal->initialized) {
1562 errno = EINVAL;
1563 TALLOC_FREE(frame);
1564 return -1;
1567 if (!fname) {
1568 errno = EINVAL;
1569 TALLOC_FREE(frame);
1570 return -1;
1573 DEBUG(4, ("smbc_mkdir(%s)\n", fname));
1575 if (SMBC_parse_path(frame,
1576 context,
1577 fname,
1578 &workgroup,
1579 &server,
1580 &port,
1581 &share,
1582 &path,
1583 &user,
1584 &password,
1585 NULL)) {
1586 errno = EINVAL;
1587 TALLOC_FREE(frame);
1588 return -1;
1591 if (!user || user[0] == (char)0) {
1592 user = talloc_strdup(frame, smbc_getUser(context));
1593 if (!user) {
1594 errno = ENOMEM;
1595 TALLOC_FREE(frame);
1596 return -1;
1600 srv = SMBC_server(frame, context, True,
1601 server, port, share, &workgroup, &user, &password);
1603 if (!srv) {
1605 TALLOC_FREE(frame);
1606 return -1; /* errno set by SMBC_server */
1610 creds = context->internal->creds;
1612 /*d_printf(">>>mkdir: resolving %s\n", path);*/
1613 status = cli_resolve_path(frame, "",
1614 creds,
1615 srv->cli, path, &targetcli, &targetpath);
1616 if (!NT_STATUS_IS_OK(status)) {
1617 d_printf("Could not resolve %s\n", path);
1618 errno = ENOENT;
1619 TALLOC_FREE(frame);
1620 return -1;
1622 /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
1624 status = cli_mkdir(targetcli, targetpath);
1625 if (!NT_STATUS_IS_OK(status)) {
1626 TALLOC_FREE(frame);
1627 errno = cli_status_to_errno(status);
1628 return -1;
1632 TALLOC_FREE(frame);
1633 return 0;
1638 * Our list function simply checks to see if a directory is not empty
1641 static NTSTATUS
1642 rmdir_list_fn(struct file_info *finfo,
1643 const char *mask,
1644 void *state)
1646 if (strncmp(finfo->name, ".", 1) != 0 &&
1647 strncmp(finfo->name, "..", 2) != 0) {
1648 bool *smbc_rmdir_dirempty = (bool *)state;
1649 *smbc_rmdir_dirempty = false;
1651 return NT_STATUS_OK;
1655 * Routine to remove a directory
1659 SMBC_rmdir_ctx(SMBCCTX *context,
1660 const char *fname)
1662 SMBCSRV *srv = NULL;
1663 char *server = NULL;
1664 char *share = NULL;
1665 char *user = NULL;
1666 char *password = NULL;
1667 char *workgroup = NULL;
1668 char *path = NULL;
1669 char *targetpath = NULL;
1670 uint16_t port = 0;
1671 struct cli_state *targetcli = NULL;
1672 struct cli_credentials *creds = NULL;
1673 TALLOC_CTX *frame = talloc_stackframe();
1674 NTSTATUS status;
1676 if (!context || !context->internal->initialized) {
1677 errno = EINVAL;
1678 TALLOC_FREE(frame);
1679 return -1;
1682 if (!fname) {
1683 errno = EINVAL;
1684 TALLOC_FREE(frame);
1685 return -1;
1688 DEBUG(4, ("smbc_rmdir(%s)\n", fname));
1690 if (SMBC_parse_path(frame,
1691 context,
1692 fname,
1693 &workgroup,
1694 &server,
1695 &port,
1696 &share,
1697 &path,
1698 &user,
1699 &password,
1700 NULL)) {
1701 errno = EINVAL;
1702 TALLOC_FREE(frame);
1703 return -1;
1706 if (!user || user[0] == (char)0) {
1707 user = talloc_strdup(frame, smbc_getUser(context));
1708 if (!user) {
1709 errno = ENOMEM;
1710 TALLOC_FREE(frame);
1711 return -1;
1715 srv = SMBC_server(frame, context, True,
1716 server, port, share, &workgroup, &user, &password);
1718 if (!srv) {
1720 TALLOC_FREE(frame);
1721 return -1; /* errno set by SMBC_server */
1725 creds = context->internal->creds;
1727 /*d_printf(">>>rmdir: resolving %s\n", path);*/
1728 status = cli_resolve_path(frame, "",
1729 creds,
1730 srv->cli, path, &targetcli, &targetpath);
1731 if (!NT_STATUS_IS_OK(status)) {
1732 d_printf("Could not resolve %s\n", path);
1733 errno = ENOENT;
1734 TALLOC_FREE(frame);
1735 return -1;
1737 /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
1739 status = cli_rmdir(targetcli, targetpath);
1741 if (!NT_STATUS_IS_OK(status)) {
1743 errno = cli_status_to_errno(status);
1745 if (errno == EACCES) { /* Check if the dir empty or not */
1747 /* Local storage to avoid buffer overflows */
1748 char *lpath;
1749 bool smbc_rmdir_dirempty = true;
1751 lpath = talloc_asprintf(frame, "%s\\*",
1752 targetpath);
1753 if (!lpath) {
1754 errno = ENOMEM;
1755 TALLOC_FREE(frame);
1756 return -1;
1759 status = cli_list(targetcli, lpath,
1760 FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1761 rmdir_list_fn,
1762 &smbc_rmdir_dirempty);
1764 if (!NT_STATUS_IS_OK(status)) {
1765 /* Fix errno to ignore latest error ... */
1766 DBG_INFO("cli_list returned an error: %s\n",
1767 nt_errstr(status));
1768 errno = EACCES;
1772 if (smbc_rmdir_dirempty)
1773 errno = EACCES;
1774 else
1775 errno = ENOTEMPTY;
1779 TALLOC_FREE(frame);
1780 return -1;
1784 TALLOC_FREE(frame);
1785 return 0;
1790 * Routine to return the current directory position
1793 off_t
1794 SMBC_telldir_ctx(SMBCCTX *context,
1795 SMBCFILE *dir)
1797 TALLOC_CTX *frame = talloc_stackframe();
1799 if (!context || !context->internal->initialized) {
1801 errno = EINVAL;
1802 TALLOC_FREE(frame);
1803 return -1;
1807 if (!SMBC_dlist_contains(context->internal->files, dir)) {
1809 errno = EBADF;
1810 TALLOC_FREE(frame);
1811 return -1;
1815 if (dir->file != False) { /* FIXME, should be dir, perhaps */
1817 errno = ENOTDIR;
1818 TALLOC_FREE(frame);
1819 return -1;
1823 /* See if we're already at the end. */
1824 if (dir->dir_next == NULL) {
1825 /* We are. */
1826 TALLOC_FREE(frame);
1827 return -1;
1831 * We return the pointer here as the offset
1833 TALLOC_FREE(frame);
1834 return (off_t)(long)dir->dir_next->dirent;
1838 * A routine to run down the list and see if the entry is OK
1839 * Modifies the dir list and the dirplus list (if it exists)
1840 * to point at the correct next entry on success.
1843 static bool update_dir_ents(SMBCFILE *dir, struct smbc_dirent *dirent)
1845 struct smbc_dir_list *tmp_dir = dir->dir_list;
1846 struct smbc_dirplus_list *tmp_dirplus = dir->dirplus_list;
1849 * Run down the list looking for what we want.
1850 * If we're enumerating files both dir_list
1851 * and dirplus_list contain the same entry
1852 * list, as they were seeded from the same
1853 * cli_list callback.
1855 * If we're enumerating servers then
1856 * dirplus_list will be NULL, so don't
1857 * update in that case.
1860 while (tmp_dir != NULL) {
1861 if (tmp_dir->dirent == dirent) {
1862 dir->dir_next = tmp_dir;
1863 if (tmp_dirplus != NULL) {
1864 dir->dirplus_next = tmp_dirplus;
1866 return true;
1868 tmp_dir = tmp_dir->next;
1869 if (tmp_dirplus != NULL) {
1870 tmp_dirplus = tmp_dirplus->next;
1873 return false;
1877 * Routine to seek on a directory
1881 SMBC_lseekdir_ctx(SMBCCTX *context,
1882 SMBCFILE *dir,
1883 off_t offset)
1885 long int l_offset = offset; /* Handle problems of size */
1886 struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
1887 TALLOC_CTX *frame = talloc_stackframe();
1888 bool ok;
1890 if (!context || !context->internal->initialized) {
1892 errno = EINVAL;
1893 TALLOC_FREE(frame);
1894 return -1;
1898 if (dir->file != False) { /* FIXME, should be dir, perhaps */
1900 errno = ENOTDIR;
1901 TALLOC_FREE(frame);
1902 return -1;
1906 /* Now, check what we were passed and see if it is OK ... */
1908 if (dirent == NULL) { /* Seek to the beginning of the list */
1910 dir->dir_next = dir->dir_list;
1912 /* Do the same for dirplus. */
1913 dir->dirplus_next = dir->dirplus_list;
1915 TALLOC_FREE(frame);
1916 return 0;
1920 if (offset == -1) { /* Seek to the end of the list */
1921 dir->dir_next = NULL;
1923 /* Do the same for dirplus. */
1924 dir->dirplus_next = NULL;
1926 TALLOC_FREE(frame);
1927 return 0;
1931 * Run down the list and make sure that the entry is OK.
1932 * Update the position of both dir and dirplus lists.
1935 ok = update_dir_ents(dir, dirent);
1936 if (!ok) {
1937 errno = EINVAL; /* Bad entry */
1938 TALLOC_FREE(frame);
1939 return -1;
1942 TALLOC_FREE(frame);
1943 return 0;
1947 * Routine to fstat a dir
1951 SMBC_fstatdir_ctx(SMBCCTX *context,
1952 SMBCFILE *dir,
1953 struct stat *st)
1956 if (!context || !context->internal->initialized) {
1958 errno = EINVAL;
1959 return -1;
1962 /* No code yet ... */
1963 return 0;
1967 SMBC_chmod_ctx(SMBCCTX *context,
1968 const char *fname,
1969 mode_t newmode)
1971 SMBCSRV *srv = NULL;
1972 char *server = NULL;
1973 char *share = NULL;
1974 char *user = NULL;
1975 char *password = NULL;
1976 char *workgroup = NULL;
1977 char *targetpath = NULL;
1978 struct cli_state *targetcli = NULL;
1979 char *path = NULL;
1980 uint32_t attr;
1981 uint16_t port = 0;
1982 struct cli_credentials *creds = NULL;
1983 TALLOC_CTX *frame = talloc_stackframe();
1984 NTSTATUS status;
1986 if (!context || !context->internal->initialized) {
1988 errno = EINVAL; /* Best I can think of ... */
1989 TALLOC_FREE(frame);
1990 return -1;
1993 if (!fname) {
1994 errno = EINVAL;
1995 TALLOC_FREE(frame);
1996 return -1;
1999 DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, (unsigned int)newmode));
2001 if (SMBC_parse_path(frame,
2002 context,
2003 fname,
2004 &workgroup,
2005 &server,
2006 &port,
2007 &share,
2008 &path,
2009 &user,
2010 &password,
2011 NULL)) {
2012 errno = EINVAL;
2013 TALLOC_FREE(frame);
2014 return -1;
2017 if (!user || user[0] == (char)0) {
2018 user = talloc_strdup(frame, smbc_getUser(context));
2019 if (!user) {
2020 errno = ENOMEM;
2021 TALLOC_FREE(frame);
2022 return -1;
2026 srv = SMBC_server(frame, context, True,
2027 server, port, share, &workgroup, &user, &password);
2029 if (!srv) {
2030 TALLOC_FREE(frame);
2031 return -1; /* errno set by SMBC_server */
2034 creds = context->internal->creds;
2036 /*d_printf(">>>unlink: resolving %s\n", path);*/
2037 status = cli_resolve_path(frame, "",
2038 creds,
2039 srv->cli, path, &targetcli, &targetpath);
2040 if (!NT_STATUS_IS_OK(status)) {
2041 d_printf("Could not resolve %s\n", path);
2042 errno = ENOENT;
2043 TALLOC_FREE(frame);
2044 return -1;
2047 attr = 0;
2049 if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) attr |= FILE_ATTRIBUTE_READONLY;
2050 if ((newmode & S_IXUSR) && lp_map_archive(-1)) attr |= FILE_ATTRIBUTE_ARCHIVE;
2051 if ((newmode & S_IXGRP) && lp_map_system(-1)) attr |= FILE_ATTRIBUTE_SYSTEM;
2052 if ((newmode & S_IXOTH) && lp_map_hidden(-1)) attr |= FILE_ATTRIBUTE_HIDDEN;
2054 status = cli_setatr(targetcli, targetpath, attr, 0);
2055 if (!NT_STATUS_IS_OK(status)) {
2056 TALLOC_FREE(frame);
2057 errno = cli_status_to_errno(status);
2058 return -1;
2061 TALLOC_FREE(frame);
2062 return 0;
2066 SMBC_utimes_ctx(SMBCCTX *context,
2067 const char *fname,
2068 struct timeval *tbuf)
2070 SMBCSRV *srv = NULL;
2071 char *server = NULL;
2072 char *share = NULL;
2073 char *user = NULL;
2074 char *password = NULL;
2075 char *workgroup = NULL;
2076 char *path = NULL;
2077 struct timespec access_time, write_time;
2078 uint16_t port = 0;
2079 TALLOC_CTX *frame = talloc_stackframe();
2080 bool ok;
2082 if (!context || !context->internal->initialized) {
2084 errno = EINVAL; /* Best I can think of ... */
2085 TALLOC_FREE(frame);
2086 return -1;
2089 if (!fname) {
2090 errno = EINVAL;
2091 TALLOC_FREE(frame);
2092 return -1;
2095 if (tbuf == NULL) {
2096 access_time = write_time = timespec_current();
2097 } else {
2098 access_time = convert_timeval_to_timespec(tbuf[0]);
2099 write_time = convert_timeval_to_timespec(tbuf[1]);
2102 if (DEBUGLVL(4)) {
2103 struct timeval_buf abuf, wbuf;
2105 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
2106 fname,
2107 timespec_string_buf(&access_time, false, &abuf),
2108 timespec_string_buf(&write_time, false, &wbuf));
2111 if (SMBC_parse_path(frame,
2112 context,
2113 fname,
2114 &workgroup,
2115 &server,
2116 &port,
2117 &share,
2118 &path,
2119 &user,
2120 &password,
2121 NULL)) {
2122 errno = EINVAL;
2123 TALLOC_FREE(frame);
2124 return -1;
2127 if (!user || user[0] == (char)0) {
2128 user = talloc_strdup(frame, smbc_getUser(context));
2129 if (!user) {
2130 errno = ENOMEM;
2131 TALLOC_FREE(frame);
2132 return -1;
2136 srv = SMBC_server(frame, context, True,
2137 server, port, share, &workgroup, &user, &password);
2139 if (!srv) {
2140 TALLOC_FREE(frame);
2141 return -1; /* errno set by SMBC_server */
2144 ok = SMBC_setatr(
2145 context,
2146 srv,
2147 path,
2148 (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT },
2149 access_time,
2150 write_time,
2151 (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT },
2153 if (!ok) {
2154 TALLOC_FREE(frame);
2155 return -1; /* errno set by SMBC_setatr */
2158 TALLOC_FREE(frame);
2159 return 0;
2163 * Routine to unlink() a file
2167 SMBC_unlink_ctx(SMBCCTX *context,
2168 const char *fname)
2170 char *server = NULL;
2171 char *share = NULL;
2172 char *user = NULL;
2173 char *password = NULL;
2174 char *workgroup = NULL;
2175 char *path = NULL;
2176 char *targetpath = NULL;
2177 uint16_t port = 0;
2178 struct cli_state *targetcli = NULL;
2179 SMBCSRV *srv = NULL;
2180 struct cli_credentials *creds = NULL;
2181 TALLOC_CTX *frame = talloc_stackframe();
2182 NTSTATUS status;
2184 if (!context || !context->internal->initialized) {
2186 errno = EINVAL; /* Best I can think of ... */
2187 TALLOC_FREE(frame);
2188 return -1;
2192 if (!fname) {
2193 errno = EINVAL;
2194 TALLOC_FREE(frame);
2195 return -1;
2199 if (SMBC_parse_path(frame,
2200 context,
2201 fname,
2202 &workgroup,
2203 &server,
2204 &port,
2205 &share,
2206 &path,
2207 &user,
2208 &password,
2209 NULL)) {
2210 errno = EINVAL;
2211 TALLOC_FREE(frame);
2212 return -1;
2215 if (!user || user[0] == (char)0) {
2216 user = talloc_strdup(frame, smbc_getUser(context));
2217 if (!user) {
2218 errno = ENOMEM;
2219 TALLOC_FREE(frame);
2220 return -1;
2224 srv = SMBC_server(frame, context, True,
2225 server, port, share, &workgroup, &user, &password);
2227 if (!srv) {
2228 TALLOC_FREE(frame);
2229 return -1; /* SMBC_server sets errno */
2233 creds = context->internal->creds;
2235 /*d_printf(">>>unlink: resolving %s\n", path);*/
2236 status = cli_resolve_path(frame, "",
2237 creds,
2238 srv->cli, path, &targetcli, &targetpath);
2239 if (!NT_STATUS_IS_OK(status)) {
2240 d_printf("Could not resolve %s\n", path);
2241 errno = ENOENT;
2242 TALLOC_FREE(frame);
2243 return -1;
2245 /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
2247 status = cli_unlink(
2248 targetcli,
2249 targetpath,
2250 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
2252 if (!NT_STATUS_IS_OK(status)) {
2254 errno = cli_status_to_errno(status);
2256 if (errno == EACCES) { /* Check if the file is a directory */
2258 int saverr = errno;
2259 struct stat sb = {0};
2261 status = SMBC_getatr(context, srv, path, &sb);
2262 if (!NT_STATUS_IS_OK(status)) {
2263 /* Hmmm, bad error ... What? */
2265 TALLOC_FREE(frame);
2266 errno = cli_status_to_errno(status);
2267 return -1;
2270 else {
2272 if (S_ISDIR(sb.st_mode))
2273 errno = EISDIR;
2274 else
2275 errno = saverr; /* Restore this */
2280 TALLOC_FREE(frame);
2281 return -1;
2285 TALLOC_FREE(frame);
2286 return 0; /* Success ... */
2291 * Routine to rename() a file
2295 SMBC_rename_ctx(SMBCCTX *ocontext,
2296 const char *oname,
2297 SMBCCTX *ncontext,
2298 const char *nname)
2300 char *server1 = NULL;
2301 char *share1 = NULL;
2302 char *server2 = NULL;
2303 char *share2 = NULL;
2304 char *user1 = NULL;
2305 char *user2 = NULL;
2306 char *password1 = NULL;
2307 char *password2 = NULL;
2308 char *workgroup = NULL;
2309 char *path1 = NULL;
2310 char *path2 = NULL;
2311 char *targetpath1 = NULL;
2312 char *targetpath2 = NULL;
2313 struct cli_state *targetcli1 = NULL;
2314 struct cli_state *targetcli2 = NULL;
2315 SMBCSRV *srv = NULL;
2316 uint16_t port1 = 0;
2317 uint16_t port2 = 0;
2318 struct cli_credentials *ocreds = NULL;
2319 struct cli_credentials *ncreds = NULL;
2320 TALLOC_CTX *frame = talloc_stackframe();
2321 NTSTATUS status;
2323 if (!ocontext || !ncontext ||
2324 !ocontext->internal->initialized ||
2325 !ncontext->internal->initialized) {
2327 errno = EINVAL; /* Best I can think of ... */
2328 TALLOC_FREE(frame);
2329 return -1;
2332 if (!oname || !nname) {
2333 errno = EINVAL;
2334 TALLOC_FREE(frame);
2335 return -1;
2338 DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
2340 if (SMBC_parse_path(frame,
2341 ocontext,
2342 oname,
2343 &workgroup,
2344 &server1,
2345 &port1,
2346 &share1,
2347 &path1,
2348 &user1,
2349 &password1,
2350 NULL)) {
2351 errno = EINVAL;
2352 TALLOC_FREE(frame);
2353 return -1;
2356 if (!user1 || user1[0] == (char)0) {
2357 user1 = talloc_strdup(frame, smbc_getUser(ocontext));
2358 if (!user1) {
2359 errno = ENOMEM;
2360 TALLOC_FREE(frame);
2361 return -1;
2365 if (SMBC_parse_path(frame,
2366 ncontext,
2367 nname,
2368 NULL,
2369 &server2,
2370 &port2,
2371 &share2,
2372 &path2,
2373 &user2,
2374 &password2,
2375 NULL)) {
2376 errno = EINVAL;
2377 TALLOC_FREE(frame);
2378 return -1;
2381 if (!user2 || user2[0] == (char)0) {
2382 user2 = talloc_strdup(frame, smbc_getUser(ncontext));
2383 if (!user2) {
2384 errno = ENOMEM;
2385 TALLOC_FREE(frame);
2386 return -1;
2390 if (strcmp(server1, server2) || strcmp(share1, share2) ||
2391 strcmp(user1, user2)) {
2392 /* Can't rename across file systems, or users?? */
2393 errno = EXDEV;
2394 TALLOC_FREE(frame);
2395 return -1;
2398 srv = SMBC_server(frame, ocontext, True,
2399 server1, port1, share1, &workgroup, &user1, &password1);
2400 if (!srv) {
2401 TALLOC_FREE(frame);
2402 return -1;
2406 /* set the credentials to make DFS work */
2407 smbc_set_credentials_with_fallback(ocontext,
2408 workgroup,
2409 user1,
2410 password1);
2412 /*d_printf(">>>rename: resolving %s\n", path1);*/
2413 ocreds = ocontext->internal->creds;
2415 status = cli_resolve_path(frame, "",
2416 ocreds,
2417 srv->cli, path1, &targetcli1, &targetpath1);
2418 if (!NT_STATUS_IS_OK(status)) {
2419 d_printf("Could not resolve %s\n", path1);
2420 errno = ENOENT;
2421 TALLOC_FREE(frame);
2422 return -1;
2425 /* set the credentials to make DFS work */
2426 smbc_set_credentials_with_fallback(ncontext,
2427 workgroup,
2428 user2,
2429 password2);
2431 /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
2432 /*d_printf(">>>rename: resolving %s\n", path2);*/
2433 ncreds = ncontext->internal->creds;
2435 status = cli_resolve_path(frame, "",
2436 ncreds,
2437 srv->cli, path2, &targetcli2, &targetpath2);
2438 if (!NT_STATUS_IS_OK(status)) {
2439 d_printf("Could not resolve %s\n", path2);
2440 errno = ENOENT;
2441 TALLOC_FREE(frame);
2442 return -1;
2444 /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
2446 if (strcmp(smbXcli_conn_remote_name(targetcli1->conn), smbXcli_conn_remote_name(targetcli2->conn)) ||
2447 strcmp(targetcli1->share, targetcli2->share))
2449 /* can't rename across file systems */
2450 errno = EXDEV;
2451 TALLOC_FREE(frame);
2452 return -1;
2455 status = cli_rename(targetcli1, targetpath1, targetpath2, false);
2456 if (!NT_STATUS_IS_OK(status)) {
2457 int eno = cli_status_to_errno(status);
2459 if (eno != EEXIST ||
2460 !NT_STATUS_IS_OK(cli_unlink(targetcli1, targetpath2,
2461 FILE_ATTRIBUTE_SYSTEM |
2462 FILE_ATTRIBUTE_HIDDEN)) ||
2463 !NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1,
2464 targetpath2, false))) {
2466 errno = eno;
2467 TALLOC_FREE(frame);
2468 return -1;
2473 TALLOC_FREE(frame);
2474 return 0; /* Success */
2477 struct smbc_notify_cb_state {
2478 struct tevent_context *ev;
2479 struct cli_state *cli;
2480 uint16_t fnum;
2481 bool recursive;
2482 uint32_t completion_filter;
2483 unsigned callback_timeout_ms;
2484 smbc_notify_callback_fn cb;
2485 void *private_data;
2488 static void smbc_notify_cb_got_changes(struct tevent_req *subreq);
2489 static void smbc_notify_cb_timedout(struct tevent_req *subreq);
2491 static struct tevent_req *smbc_notify_cb_send(
2492 TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
2493 uint16_t fnum, bool recursive, uint32_t completion_filter,
2494 unsigned callback_timeout_ms,
2495 smbc_notify_callback_fn cb, void *private_data)
2497 struct tevent_req *req, *subreq;
2498 struct smbc_notify_cb_state *state;
2500 req = tevent_req_create(mem_ctx, &state, struct smbc_notify_cb_state);
2501 if (req == NULL) {
2502 return NULL;
2504 state->ev = ev;
2505 state->cli = cli;
2506 state->fnum = fnum;
2507 state->recursive = recursive;
2508 state->completion_filter = completion_filter;
2509 state->callback_timeout_ms = callback_timeout_ms;
2510 state->cb = cb;
2511 state->private_data = private_data;
2513 subreq = cli_notify_send(
2514 state, state->ev, state->cli, state->fnum, 1000,
2515 state->completion_filter, state->recursive);
2516 if (tevent_req_nomem(subreq, req)) {
2517 return tevent_req_post(req, ev);
2519 tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req);
2521 if (state->callback_timeout_ms == 0) {
2522 return req;
2525 subreq = tevent_wakeup_send(
2526 state, state->ev,
2527 tevent_timeval_current_ofs(state->callback_timeout_ms/1000,
2528 state->callback_timeout_ms*1000));
2529 if (tevent_req_nomem(subreq, req)) {
2530 return tevent_req_post(req, ev);
2532 tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req);
2534 return req;
2537 static void smbc_notify_cb_got_changes(struct tevent_req *subreq)
2539 struct tevent_req *req = tevent_req_callback_data(
2540 subreq, struct tevent_req);
2541 struct smbc_notify_cb_state *state = tevent_req_data(
2542 req, struct smbc_notify_cb_state);
2543 uint32_t num_changes;
2544 struct notify_change *changes;
2545 NTSTATUS status;
2546 int cb_ret;
2548 status = cli_notify_recv(subreq, state, &num_changes, &changes);
2549 TALLOC_FREE(subreq);
2550 if (tevent_req_nterror(req, status)) {
2551 return;
2555 struct smbc_notify_callback_action actions[num_changes];
2556 uint32_t i;
2558 for (i=0; i<num_changes; i++) {
2559 actions[i].action = changes[i].action;
2560 actions[i].filename = changes[i].name;
2563 cb_ret = state->cb(actions, num_changes, state->private_data);
2566 TALLOC_FREE(changes);
2568 if (cb_ret != 0) {
2569 tevent_req_done(req);
2570 return;
2573 subreq = cli_notify_send(
2574 state, state->ev, state->cli, state->fnum, 1000,
2575 state->completion_filter, state->recursive);
2576 if (tevent_req_nomem(subreq, req)) {
2577 return;
2579 tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req);
2582 static void smbc_notify_cb_timedout(struct tevent_req *subreq)
2584 struct tevent_req *req = tevent_req_callback_data(
2585 subreq, struct tevent_req);
2586 struct smbc_notify_cb_state *state = tevent_req_data(
2587 req, struct smbc_notify_cb_state);
2588 int cb_ret;
2589 bool ok;
2591 ok = tevent_wakeup_recv(subreq);
2592 TALLOC_FREE(subreq);
2593 if (!ok) {
2594 tevent_req_oom(req);
2595 return;
2598 cb_ret = state->cb(NULL, 0, state->private_data);
2599 if (cb_ret != 0) {
2600 tevent_req_done(req);
2601 return;
2604 subreq = tevent_wakeup_send(
2605 state, state->ev,
2606 tevent_timeval_current_ofs(state->callback_timeout_ms/1000,
2607 state->callback_timeout_ms*1000));
2608 if (tevent_req_nomem(subreq, req)) {
2609 return;
2611 tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req);
2614 static NTSTATUS smbc_notify_cb_recv(struct tevent_req *req)
2616 return tevent_req_simple_recv_ntstatus(req);
2619 static NTSTATUS smbc_notify_cb(struct cli_state *cli, uint16_t fnum,
2620 bool recursive, uint32_t completion_filter,
2621 unsigned callback_timeout_ms,
2622 smbc_notify_callback_fn cb, void *private_data)
2624 TALLOC_CTX *frame = talloc_stackframe();
2625 struct tevent_context *ev;
2626 struct tevent_req *req;
2627 NTSTATUS status = NT_STATUS_NO_MEMORY;
2629 ev = samba_tevent_context_init(frame);
2630 if (ev == NULL) {
2631 goto fail;
2633 req = smbc_notify_cb_send(frame, ev, cli, fnum, recursive,
2634 completion_filter,
2635 callback_timeout_ms, cb, private_data);
2636 if (req == NULL) {
2637 goto fail;
2639 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2640 goto fail;
2642 status = smbc_notify_cb_recv(req);
2643 TALLOC_FREE(req);
2644 fail:
2645 TALLOC_FREE(frame);
2646 return status;
2650 SMBC_notify_ctx(SMBCCTX *context, SMBCFILE *dir, smbc_bool recursive,
2651 uint32_t completion_filter, unsigned callback_timeout_ms,
2652 smbc_notify_callback_fn cb, void *private_data)
2654 TALLOC_CTX *frame = talloc_stackframe();
2655 struct cli_state *cli;
2656 char *server = NULL;
2657 char *share = NULL;
2658 char *user = NULL;
2659 char *password = NULL;
2660 char *options = NULL;
2661 char *workgroup = NULL;
2662 char *path = NULL;
2663 uint16_t port;
2664 NTSTATUS status;
2665 uint16_t fnum;
2667 if ((context == NULL) || !context->internal->initialized) {
2668 TALLOC_FREE(frame);
2669 errno = EINVAL;
2670 return -1;
2672 if (!SMBC_dlist_contains(context->internal->files, dir)) {
2673 TALLOC_FREE(frame);
2674 errno = EBADF;
2675 return -1;
2678 if (SMBC_parse_path(frame,
2679 context,
2680 dir->fname,
2681 &workgroup,
2682 &server,
2683 &port,
2684 &share,
2685 &path,
2686 &user,
2687 &password,
2688 &options)) {
2689 DEBUG(4, ("no valid path\n"));
2690 TALLOC_FREE(frame);
2691 errno = EINVAL + 8194;
2692 return -1;
2695 DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2696 "path='%s' options='%s'\n",
2697 dir->fname, server, share, path, options));
2699 DEBUG(4, ("%s(%p, %d, %"PRIu32")\n", __func__, dir,
2700 (int)recursive, completion_filter));
2702 cli = dir->srv->cli;
2703 status = cli_ntcreate(
2704 cli, path, 0, FILE_READ_DATA, 0,
2705 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2706 FILE_OPEN, 0, 0, &fnum, NULL);
2707 if (!NT_STATUS_IS_OK(status)) {
2708 TALLOC_FREE(frame);
2709 errno = cli_status_to_errno(status);
2710 return -1;
2713 status = smbc_notify_cb(cli, fnum, recursive != 0, completion_filter,
2714 callback_timeout_ms, cb, private_data);
2715 if (!NT_STATUS_IS_OK(status)) {
2716 cli_close(cli, fnum);
2717 TALLOC_FREE(frame);
2718 errno = cli_status_to_errno(status);
2719 return -1;
2722 cli_close(cli, fnum);
2724 TALLOC_FREE(frame);
2725 return 0;