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