d131c30c772348b9b0267bc66dbf423da6c369d1
[samba.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/libsmb.h"
27 #include "auth_info.h"
28 #include "libsmbclient.h"
29 #include "libsmb_internal.h"
30 #include "rpc_client/cli_pipe.h"
31 #include "../librpc/gen_ndr/ndr_srvsvc_c.h"
32 #include "libsmb/nmblib.h"
33 #include "../libcli/smb/smbXcli_base.h"
34
35 /*
36  * Routine to open a directory
37  * We accept the URL syntax explained in SMBC_parse_path(), above.
38  */
39
40 static void
41 remove_dir(SMBCFILE *dir)
42 {
43         struct smbc_dir_list *d,*f;
44
45         d = dir->dir_list;
46         while (d) {
47
48                 f = d; d = d->next;
49
50                 SAFE_FREE(f->dirent);
51                 SAFE_FREE(f);
52
53         }
54
55         dir->dir_list = dir->dir_end = dir->dir_next = NULL;
56
57 }
58
59 static int
60 add_dirent(SMBCFILE *dir,
61            const char *name,
62            const char *comment,
63            uint32_t type)
64 {
65         struct smbc_dirent *dirent;
66         int size;
67         int name_length = (name == NULL ? 0 : strlen(name));
68         int comment_len = (comment == NULL ? 0 : strlen(comment));
69
70         /*
71          * Allocate space for the dirent, which must be increased by the
72          * size of the name and the comment and 1 each for the null terminator.
73          */
74
75         size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
76
77         dirent = (struct smbc_dirent *)SMB_MALLOC(size);
78
79         if (!dirent) {
80
81                 dir->dir_error = ENOMEM;
82                 return -1;
83
84         }
85
86         ZERO_STRUCTP(dirent);
87
88         if (dir->dir_list == NULL) {
89
90                 dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
91                 if (!dir->dir_list) {
92
93                         SAFE_FREE(dirent);
94                         dir->dir_error = ENOMEM;
95                         return -1;
96
97                 }
98                 ZERO_STRUCTP(dir->dir_list);
99
100                 dir->dir_end = dir->dir_next = dir->dir_list;
101         }
102         else {
103
104                 dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
105
106                 if (!dir->dir_end->next) {
107
108                         SAFE_FREE(dirent);
109                         dir->dir_error = ENOMEM;
110                         return -1;
111
112                 }
113                 ZERO_STRUCTP(dir->dir_end->next);
114
115                 dir->dir_end = dir->dir_end->next;
116         }
117
118         dir->dir_end->next = NULL;
119         dir->dir_end->dirent = dirent;
120
121         dirent->smbc_type = type;
122         dirent->namelen = name_length;
123         dirent->commentlen = comment_len;
124         dirent->dirlen = size;
125
126         /*
127          * dirent->namelen + 1 includes the null (no null termination needed)
128          * Ditto for dirent->commentlen.
129          * The space for the two null bytes was allocated.
130          */
131         strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
132         dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
133         strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
134
135         return 0;
136
137 }
138
139 static void
140 list_unique_wg_fn(const char *name,
141                   uint32_t type,
142                   const char *comment,
143                   void *state)
144 {
145         SMBCFILE *dir = (SMBCFILE *)state;
146         struct smbc_dir_list *dir_list;
147         struct smbc_dirent *dirent;
148         int dirent_type;
149         int do_remove = 0;
150
151         dirent_type = dir->dir_type;
152
153         if (add_dirent(dir, name, comment, dirent_type) < 0) {
154                 /* An error occurred, what do we do? */
155                 /* FIXME: Add some code here */
156                 /* Change cli_NetServerEnum to take a fn
157                    returning NTSTATUS... JRA. */
158         }
159
160         /* Point to the one just added */
161         dirent = dir->dir_end->dirent;
162
163         /* See if this was a duplicate */
164         for (dir_list = dir->dir_list;
165              dir_list != dir->dir_end;
166              dir_list = dir_list->next) {
167                 if (! do_remove &&
168                     strcmp(dir_list->dirent->name, dirent->name) == 0) {
169                         /* Duplicate.  End end of list need to be removed. */
170                         do_remove = 1;
171                 }
172
173                 if (do_remove && dir_list->next == dir->dir_end) {
174                         /* Found the end of the list.  Remove it. */
175                         dir->dir_end = dir_list;
176                         free(dir_list->next);
177                         free(dirent);
178                         dir_list->next = NULL;
179                         break;
180                 }
181         }
182 }
183
184 static void
185 list_fn(const char *name,
186         uint32_t type,
187         const char *comment,
188         void *state)
189 {
190         SMBCFILE *dir = (SMBCFILE *)state;
191         int dirent_type;
192
193         /*
194          * We need to process the type a little ...
195          *
196          * Disk share     = 0x00000000
197          * Print share    = 0x00000001
198          * Comms share    = 0x00000002 (obsolete?)
199          * IPC$ share     = 0x00000003
200          *
201          * administrative shares:
202          * ADMIN$, IPC$, C$, D$, E$ ...  are type |= 0x80000000
203          */
204
205         if (dir->dir_type == SMBC_FILE_SHARE) {
206                 switch (type) {
207                 case 0 | 0x80000000:
208                 case 0:
209                         dirent_type = SMBC_FILE_SHARE;
210                         break;
211
212                 case 1:
213                         dirent_type = SMBC_PRINTER_SHARE;
214                         break;
215
216                 case 2:
217                         dirent_type = SMBC_COMMS_SHARE;
218                         break;
219
220                 case 3 | 0x80000000:
221                 case 3:
222                         dirent_type = SMBC_IPC_SHARE;
223                         break;
224
225                 default:
226                         dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
227                         break;
228                 }
229         }
230         else {
231                 dirent_type = dir->dir_type;
232         }
233
234         if (add_dirent(dir, name, comment, dirent_type) < 0) {
235                 /* An error occurred, what do we do? */
236                 /* FIXME: Add some code here */
237                 /* Change cli_NetServerEnum to take a fn
238                    returning NTSTATUS... JRA. */
239         }
240 }
241
242 static NTSTATUS
243 dir_list_fn(const char *mnt,
244             struct file_info *finfo,
245             const char *mask,
246             void *state)
247 {
248
249         if (add_dirent((SMBCFILE *)state, finfo->name, "",
250                        (finfo->mode&FILE_ATTRIBUTE_DIRECTORY?SMBC_DIR:SMBC_FILE)) < 0) {
251                 SMBCFILE *dir = (SMBCFILE *)state;
252                 return map_nt_error_from_unix(dir->dir_error);
253         }
254         return NT_STATUS_OK;
255 }
256
257 static int
258 net_share_enum_rpc(struct cli_state *cli,
259                    void (*fn)(const char *name,
260                               uint32_t type,
261                               const char *comment,
262                               void *state),
263                    void *state)
264 {
265         int i;
266         WERROR result;
267         uint32_t preferred_len = 0xffffffff;
268         uint32_t type;
269         struct srvsvc_NetShareInfoCtr info_ctr;
270         struct srvsvc_NetShareCtr1 ctr1;
271         fstring name = "";
272         fstring comment = "";
273         struct rpc_pipe_client *pipe_hnd = NULL;
274         NTSTATUS nt_status;
275         uint32_t resume_handle = 0;
276         uint32_t total_entries = 0;
277         struct dcerpc_binding_handle *b;
278
279         /* Open the server service pipe */
280         nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc,
281                                              &pipe_hnd);
282         if (!NT_STATUS_IS_OK(nt_status)) {
283                 DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
284                 return -1;
285         }
286
287         ZERO_STRUCT(info_ctr);
288         ZERO_STRUCT(ctr1);
289
290         info_ctr.level = 1;
291         info_ctr.ctr.ctr1 = &ctr1;
292
293         b = pipe_hnd->binding_handle;
294
295         /* Issue the NetShareEnum RPC call and retrieve the response */
296         nt_status = dcerpc_srvsvc_NetShareEnumAll(b, talloc_tos(),
297                                                   pipe_hnd->desthost,
298                                                   &info_ctr,
299                                                   preferred_len,
300                                                   &total_entries,
301                                                   &resume_handle,
302                                                   &result);
303
304         /* Was it successful? */
305         if (!NT_STATUS_IS_OK(nt_status)) {
306                 /*  Nope.  Go clean up. */
307                 result = ntstatus_to_werror(nt_status);
308                 goto done;
309         }
310
311         if (!W_ERROR_IS_OK(result)) {
312                 /*  Nope.  Go clean up. */
313                 goto done;
314         }
315
316         if (total_entries == 0) {
317                 /*  Nope.  Go clean up. */
318                 result = WERR_GENERAL_FAILURE;
319                 goto done;
320         }
321
322         /* For each returned entry... */
323         for (i = 0; i < info_ctr.ctr.ctr1->count; i++) {
324
325                 /* pull out the share name */
326                 fstrcpy(name, info_ctr.ctr.ctr1->array[i].name);
327
328                 /* pull out the share's comment */
329                 fstrcpy(comment, info_ctr.ctr.ctr1->array[i].comment);
330
331                 /* Get the type value */
332                 type = info_ctr.ctr.ctr1->array[i].type;
333
334                 /* Add this share to the list */
335                 (*fn)(name, type, comment, state);
336         }
337
338 done:
339         /* Close the server service pipe */
340         TALLOC_FREE(pipe_hnd);
341
342         /* Tell 'em if it worked */
343         return W_ERROR_IS_OK(result) ? 0 : -1;
344 }
345
346
347 /*
348  * Verify that the options specified in a URL are valid
349  */
350 int
351 SMBC_check_options(char *server,
352                    char *share,
353                    char *path,
354                    char *options)
355 {
356         DEBUG(4, ("SMBC_check_options(): server='%s' share='%s' "
357                   "path='%s' options='%s'\n",
358                   server, share, path, options));
359
360         /* No options at all is always ok */
361         if (! *options) return 0;
362
363         /* Currently, we don't support any options. */
364         return -1;
365 }
366
367
368 SMBCFILE *
369 SMBC_opendir_ctx(SMBCCTX *context,
370                  const char *fname)
371 {
372         int saved_errno;
373         char *server = NULL;
374         char *share = NULL;
375         char *user = NULL;
376         char *password = NULL;
377         char *options = NULL;
378         char *workgroup = NULL;
379         char *path = NULL;
380         uint16_t mode;
381         uint16_t port = 0;
382         char *p = NULL;
383         SMBCSRV *srv  = NULL;
384         SMBCFILE *dir = NULL;
385         struct sockaddr_storage rem_ss;
386         TALLOC_CTX *frame = talloc_stackframe();
387
388         if (!context || !context->internal->initialized) {
389                 DEBUG(4, ("no valid context\n"));
390                 TALLOC_FREE(frame);
391                 errno = EINVAL + 8192;
392                 return NULL;
393
394         }
395
396         if (!fname) {
397                 DEBUG(4, ("no valid fname\n"));
398                 TALLOC_FREE(frame);
399                 errno = EINVAL + 8193;
400                 return NULL;
401         }
402
403         if (SMBC_parse_path(frame,
404                             context,
405                             fname,
406                             &workgroup,
407                             &server,
408                             &port,
409                             &share,
410                             &path,
411                             &user,
412                             &password,
413                             &options)) {
414                 DEBUG(4, ("no valid path\n"));
415                 TALLOC_FREE(frame);
416                 errno = EINVAL + 8194;
417                 return NULL;
418         }
419
420         DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
421                   "path='%s' options='%s'\n",
422                   fname, server, share, path, options));
423
424         /* Ensure the options are valid */
425         if (SMBC_check_options(server, share, path, options)) {
426                 DEBUG(4, ("unacceptable options (%s)\n", options));
427                 TALLOC_FREE(frame);
428                 errno = EINVAL + 8195;
429                 return NULL;
430         }
431
432         if (!user || user[0] == (char)0) {
433                 user = talloc_strdup(frame, smbc_getUser(context));
434                 if (!user) {
435                         TALLOC_FREE(frame);
436                         errno = ENOMEM;
437                         return NULL;
438                 }
439         }
440
441         dir = SMB_MALLOC_P(SMBCFILE);
442
443         if (!dir) {
444                 TALLOC_FREE(frame);
445                 errno = ENOMEM;
446                 return NULL;
447         }
448
449         ZERO_STRUCTP(dir);
450
451         dir->cli_fd   = 0;
452         dir->fname    = SMB_STRDUP(fname);
453         dir->srv      = NULL;
454         dir->offset   = 0;
455         dir->file     = False;
456         dir->dir_list = dir->dir_next = dir->dir_end = NULL;
457
458         if (server[0] == (char)0) {
459
460                 int i;
461                 int count;
462                 int max_lmb_count;
463                 struct sockaddr_storage *ip_list;
464                 struct sockaddr_storage server_addr;
465                 struct user_auth_info u_info;
466                 NTSTATUS status;
467
468                 if (share[0] != (char)0 || path[0] != (char)0) {
469
470                         if (dir) {
471                                 SAFE_FREE(dir->fname);
472                                 SAFE_FREE(dir);
473                         }
474                         TALLOC_FREE(frame);
475                         errno = EINVAL + 8196;
476                         return NULL;
477                 }
478
479                 /* Determine how many local master browsers to query */
480                 max_lmb_count = (smbc_getOptionBrowseMaxLmbCount(context) == 0
481                                  ? INT_MAX
482                                  : smbc_getOptionBrowseMaxLmbCount(context));
483
484                 memset(&u_info, '\0', sizeof(u_info));
485                 u_info.username = talloc_strdup(frame,user);
486                 u_info.password = talloc_strdup(frame,password);
487                 if (!u_info.username || !u_info.password) {
488                         if (dir) {
489                                 SAFE_FREE(dir->fname);
490                                 SAFE_FREE(dir);
491                         }
492                         TALLOC_FREE(frame);
493                         return NULL;
494                 }
495
496                 /*
497                  * We have server and share and path empty but options
498                  * requesting that we scan all master browsers for their list
499                  * of workgroups/domains.  This implies that we must first try
500                  * broadcast queries to find all master browsers, and if that
501                  * doesn't work, then try our other methods which return only
502                  * a single master browser.
503                  */
504
505                 ip_list = NULL;
506                 status = name_resolve_bcast(MSBROWSE, 1, talloc_tos(),
507                                             &ip_list, &count);
508                 if (!NT_STATUS_IS_OK(status))
509                 {
510
511                         TALLOC_FREE(ip_list);
512
513                         if (!find_master_ip(workgroup, &server_addr)) {
514
515                                 if (dir) {
516                                         SAFE_FREE(dir->fname);
517                                         SAFE_FREE(dir);
518                                 }
519                                 TALLOC_FREE(frame);
520                                 errno = ENOENT;
521                                 return NULL;
522                         }
523
524                         ip_list = (struct sockaddr_storage *)talloc_memdup(
525                                 talloc_tos(), &server_addr,
526                                 sizeof(server_addr));
527                         if (ip_list == NULL) {
528                                 if (dir) {
529                                         SAFE_FREE(dir->fname);
530                                         SAFE_FREE(dir);
531                                 }
532                                 TALLOC_FREE(frame);
533                                 errno = ENOMEM;
534                                 return NULL;
535                         }
536                         count = 1;
537                 }
538
539                 for (i = 0; i < count && i < max_lmb_count; i++) {
540                         char addr[INET6_ADDRSTRLEN];
541                         char *wg_ptr = NULL;
542                         struct cli_state *cli = NULL;
543
544                         print_sockaddr(addr, sizeof(addr), &ip_list[i]);
545                         DEBUG(99, ("Found master browser %d of %d: %s\n",
546                                    i+1, MAX(count, max_lmb_count),
547                                    addr));
548
549                         cli = get_ipc_connect_master_ip(talloc_tos(),
550                                                         &ip_list[i],
551                                                         &u_info,
552                                                         &wg_ptr);
553                         /* cli == NULL is the master browser refused to talk or
554                            could not be found */
555                         if (!cli) {
556                                 continue;
557                         }
558
559                         workgroup = talloc_strdup(frame, wg_ptr);
560                         server = talloc_strdup(frame, smbXcli_conn_remote_name(cli->conn));
561
562                         cli_shutdown(cli);
563
564                         if (!workgroup || !server) {
565                                 if (dir) {
566                                         SAFE_FREE(dir->fname);
567                                         SAFE_FREE(dir);
568                                 }
569                                 TALLOC_FREE(frame);
570                                 errno = ENOMEM;
571                                 return NULL;
572                         }
573
574                         DEBUG(4, ("using workgroup %s %s\n",
575                                   workgroup, server));
576
577                         /*
578                          * For each returned master browser IP address, get a
579                          * connection to IPC$ on the server if we do not
580                          * already have one, and determine the
581                          * workgroups/domains that it knows about.
582                          */
583
584                         srv = SMBC_server(frame, context, True, server, port, "IPC$",
585                                           &workgroup, &user, &password);
586                         if (!srv) {
587                                 continue;
588                         }
589
590                         dir->srv = srv;
591                         dir->dir_type = SMBC_WORKGROUP;
592
593                         /* Now, list the stuff ... */
594
595                         if (!cli_NetServerEnum(srv->cli,
596                                                workgroup,
597                                                SV_TYPE_DOMAIN_ENUM,
598                                                list_unique_wg_fn,
599                                                (void *)dir)) {
600                                 continue;
601                         }
602                 }
603
604                 TALLOC_FREE(ip_list);
605         } else {
606                 /*
607                  * Server not an empty string ... Check the rest and see what
608                  * gives
609                  */
610                 if (*share == '\0') {
611                         if (*path != '\0') {
612
613                                 /* Should not have empty share with path */
614                                 if (dir) {
615                                         SAFE_FREE(dir->fname);
616                                         SAFE_FREE(dir);
617                                 }
618                                 TALLOC_FREE(frame);
619                                 errno = EINVAL + 8197;
620                                 return NULL;
621
622                         }
623
624                         /*
625                          * We don't know if <server> is really a server name
626                          * or is a workgroup/domain name.  If we already have
627                          * a server structure for it, we'll use it.
628                          * Otherwise, check to see if <server><1D>,
629                          * <server><1B>, or <server><20> translates.  We check
630                          * to see if <server> is an IP address first.
631                          */
632
633                         /*
634                          * See if we have an existing server.  Do not
635                          * establish a connection if one does not already
636                          * exist.
637                          */
638                         srv = SMBC_server(frame, context, False,
639                                           server, port, "IPC$",
640                                           &workgroup, &user, &password);
641
642                         /*
643                          * If no existing server and not an IP addr, look for
644                          * LMB or DMB
645                          */
646                         if (!srv &&
647                             !is_ipaddress(server) &&
648                             (resolve_name(server, &rem_ss, 0x1d, false) ||   /* LMB */
649                              resolve_name(server, &rem_ss, 0x1b, false) )) { /* DMB */
650                                 /*
651                                  * "server" is actually a workgroup name,
652                                  * not a server. Make this clear.
653                                  */
654                                 char *wgroup = server;
655                                 fstring buserver;
656
657                                 dir->dir_type = SMBC_SERVER;
658
659                                 /*
660                                  * Get the backup list ...
661                                  */
662                                 if (!name_status_find(wgroup, 0, 0,
663                                                       &rem_ss, buserver)) {
664                                         char addr[INET6_ADDRSTRLEN];
665
666                                         print_sockaddr(addr, sizeof(addr), &rem_ss);
667                                         DEBUG(0,("Could not get name of "
668                                                 "local/domain master browser "
669                                                 "for workgroup %s from "
670                                                 "address %s\n",
671                                                 wgroup,
672                                                 addr));
673                                         if (dir) {
674                                                 SAFE_FREE(dir->fname);
675                                                 SAFE_FREE(dir);
676                                         }
677                                         TALLOC_FREE(frame);
678                                         errno = EPERM;
679                                         return NULL;
680
681                                 }
682
683                                 /*
684                                  * Get a connection to IPC$ on the server if
685                                  * we do not already have one
686                                  */
687                                 srv = SMBC_server(frame, context, True,
688                                                   buserver, port, "IPC$",
689                                                   &workgroup,
690                                                   &user, &password);
691                                 if (!srv) {
692                                         DEBUG(0, ("got no contact to IPC$\n"));
693                                         if (dir) {
694                                                 SAFE_FREE(dir->fname);
695                                                 SAFE_FREE(dir);
696                                         }
697                                         TALLOC_FREE(frame);
698                                         return NULL;
699
700                                 }
701
702                                 dir->srv = srv;
703
704                                 /* Now, list the servers ... */
705                                 if (!cli_NetServerEnum(srv->cli, wgroup,
706                                                        0x0000FFFE, list_fn,
707                                                        (void *)dir)) {
708
709                                         if (dir) {
710                                                 SAFE_FREE(dir->fname);
711                                                 SAFE_FREE(dir);
712                                         }
713                                         TALLOC_FREE(frame);
714                                         return NULL;
715                                 }
716                         } else if (srv ||
717                                    (resolve_name(server, &rem_ss, 0x20, false))) {
718
719                                 /*
720                                  * If we hadn't found the server, get one now
721                                  */
722                                 if (!srv) {
723                                         srv = SMBC_server(frame, context, True,
724                                                           server, port, "IPC$",
725                                                           &workgroup,
726                                                           &user, &password);
727                                 }
728
729                                 if (!srv) {
730                                         if (dir) {
731                                                 SAFE_FREE(dir->fname);
732                                                 SAFE_FREE(dir);
733                                         }
734                                         TALLOC_FREE(frame);
735                                         return NULL;
736
737                                 }
738
739                                 dir->dir_type = SMBC_FILE_SHARE;
740                                 dir->srv = srv;
741
742                                 /* List the shares ... */
743
744                                 if (net_share_enum_rpc(
745                                             srv->cli,
746                                             list_fn,
747                                             (void *) dir) < 0 &&
748                                     cli_RNetShareEnum(
749                                             srv->cli,
750                                             list_fn,
751                                             (void *)dir) < 0) {
752
753                                         errno = cli_errno(srv->cli);
754                                         if (dir) {
755                                                 SAFE_FREE(dir->fname);
756                                                 SAFE_FREE(dir);
757                                         }
758                                         TALLOC_FREE(frame);
759                                         return NULL;
760
761                                 }
762                         } else {
763                                 /* Neither the workgroup nor server exists */
764                                 errno = ECONNREFUSED;
765                                 if (dir) {
766                                         SAFE_FREE(dir->fname);
767                                         SAFE_FREE(dir);
768                                 }
769                                 TALLOC_FREE(frame);
770                                 return NULL;
771                         }
772
773                 }
774                 else {
775                         /*
776                          * The server and share are specified ... work from
777                          * there ...
778                          */
779                         char *targetpath;
780                         struct cli_state *targetcli;
781                         NTSTATUS status;
782
783                         /* We connect to the server and list the directory */
784                         dir->dir_type = SMBC_FILE_SHARE;
785
786                         srv = SMBC_server(frame, context, True, server, port, share,
787                                           &workgroup, &user, &password);
788
789                         if (!srv) {
790                                 if (dir) {
791                                         SAFE_FREE(dir->fname);
792                                         SAFE_FREE(dir);
793                                 }
794                                 TALLOC_FREE(frame);
795                                 return NULL;
796                         }
797
798                         dir->srv = srv;
799
800                         /* Now, list the files ... */
801
802                         p = path + strlen(path);
803                         path = talloc_asprintf_append(path, "\\*");
804                         if (!path) {
805                                 if (dir) {
806                                         SAFE_FREE(dir->fname);
807                                         SAFE_FREE(dir);
808                                 }
809                                 TALLOC_FREE(frame);
810                                 return NULL;
811                         }
812
813                         status = cli_resolve_path(
814                                 frame, "", context->internal->auth_info,
815                                 srv->cli, path, &targetcli, &targetpath);
816                         if (!NT_STATUS_IS_OK(status)) {
817                                 d_printf("Could not resolve %s\n", path);
818                                 if (dir) {
819                                         SAFE_FREE(dir->fname);
820                                         SAFE_FREE(dir);
821                                 }
822                                 TALLOC_FREE(frame);
823                                 return NULL;
824                         }
825
826                         status = cli_list(targetcli, targetpath,
827                                           FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
828                                           dir_list_fn, (void *)dir);
829                         if (!NT_STATUS_IS_OK(status)) {
830                                 if (dir) {
831                                         SAFE_FREE(dir->fname);
832                                         SAFE_FREE(dir);
833                                 }
834                                 saved_errno = SMBC_errno(context, targetcli);
835
836                                 if (saved_errno == EINVAL) {
837                                         /*
838                                          * See if they asked to opendir
839                                          * something other than a directory.
840                                          * If so, the converted error value we
841                                          * got would have been EINVAL rather
842                                          * than ENOTDIR.
843                                          */
844                                         *p = '\0'; /* restore original path */
845
846                                         if (SMBC_getatr(context, srv, path,
847                                                         &mode, NULL,
848                                                         NULL, NULL, NULL, NULL,
849                                                         NULL) &&
850                                             ! IS_DOS_DIR(mode)) {
851
852                                                 /* It is.  Correct the error value */
853                                                 saved_errno = ENOTDIR;
854                                         }
855                                 }
856
857                                 /*
858                                  * If there was an error and the server is no
859                                  * good any more...
860                                  */
861                                 if (cli_is_error(targetcli) &&
862                                     smbc_getFunctionCheckServer(context)(context, srv)) {
863
864                                         /* ... then remove it. */
865                                         if (smbc_getFunctionRemoveUnusedServer(context)(context,
866                                                                                         srv)) {
867                                                 /*
868                                                  * We could not remove the
869                                                  * server completely, remove
870                                                  * it from the cache so we
871                                                  * will not get it again. It
872                                                  * will be removed when the
873                                                  * last file/dir is closed.
874                                                  */
875                                                 smbc_getFunctionRemoveCachedServer(context)(context, srv);
876                                         }
877                                 }
878
879                                 TALLOC_FREE(frame);
880                                 errno = saved_errno;
881                                 return NULL;
882                         }
883                 }
884
885         }
886
887         DLIST_ADD(context->internal->files, dir);
888         TALLOC_FREE(frame);
889         return dir;
890
891 }
892
893 /*
894  * Routine to close a directory
895  */
896
897 int
898 SMBC_closedir_ctx(SMBCCTX *context,
899                   SMBCFILE *dir)
900 {
901         TALLOC_CTX *frame = talloc_stackframe();
902
903         if (!context || !context->internal->initialized) {
904                 errno = EINVAL;
905                 TALLOC_FREE(frame);
906                 return -1;
907         }
908
909         if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
910                 errno = EBADF;
911                 TALLOC_FREE(frame);
912                 return -1;
913         }
914
915         remove_dir(dir); /* Clean it up */
916
917         DLIST_REMOVE(context->internal->files, dir);
918
919         if (dir) {
920
921                 SAFE_FREE(dir->fname);
922                 SAFE_FREE(dir);    /* Free the space too */
923         }
924
925         TALLOC_FREE(frame);
926         return 0;
927
928 }
929
930 static void
931 smbc_readdir_internal(SMBCCTX * context,
932                       struct smbc_dirent *dest,
933                       struct smbc_dirent *src,
934                       int max_namebuf_len)
935 {
936         if (smbc_getOptionUrlEncodeReaddirEntries(context)) {
937
938                 /* url-encode the name.  get back remaining buffer space */
939                 max_namebuf_len =
940                         smbc_urlencode(dest->name, src->name, max_namebuf_len);
941
942                 /* We now know the name length */
943                 dest->namelen = strlen(dest->name);
944
945                 /* Save the pointer to the beginning of the comment */
946                 dest->comment = dest->name + dest->namelen + 1;
947
948                 /* Copy the comment */
949                 strncpy(dest->comment, src->comment, max_namebuf_len - 1);
950                 dest->comment[max_namebuf_len - 1] = '\0';
951
952                 /* Save other fields */
953                 dest->smbc_type = src->smbc_type;
954                 dest->commentlen = strlen(dest->comment);
955                 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
956                                 (char *) dest);
957         } else {
958
959                 /* No encoding.  Just copy the entry as is. */
960                 memcpy(dest, src, src->dirlen);
961                 dest->comment = (char *)(&dest->name + src->namelen + 1);
962         }
963
964 }
965
966 /*
967  * Routine to get a directory entry
968  */
969
970 struct smbc_dirent *
971 SMBC_readdir_ctx(SMBCCTX *context,
972                  SMBCFILE *dir)
973 {
974         int maxlen;
975         struct smbc_dirent *dirp, *dirent;
976         TALLOC_CTX *frame = talloc_stackframe();
977
978         /* Check that all is ok first ... */
979
980         if (!context || !context->internal->initialized) {
981
982                 errno = EINVAL;
983                 DEBUG(0, ("Invalid context in SMBC_readdir_ctx()\n"));
984                 TALLOC_FREE(frame);
985                 return NULL;
986
987         }
988
989         if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
990
991                 errno = EBADF;
992                 DEBUG(0, ("Invalid dir in SMBC_readdir_ctx()\n"));
993                 TALLOC_FREE(frame);
994                 return NULL;
995
996         }
997
998         if (dir->file != False) { /* FIXME, should be dir, perhaps */
999
1000                 errno = ENOTDIR;
1001                 DEBUG(0, ("Found file vs directory in SMBC_readdir_ctx()\n"));
1002                 TALLOC_FREE(frame);
1003                 return NULL;
1004
1005         }
1006
1007         if (!dir->dir_next) {
1008                 TALLOC_FREE(frame);
1009                 return NULL;
1010         }
1011
1012         dirent = dir->dir_next->dirent;
1013         if (!dirent) {
1014
1015                 errno = ENOENT;
1016                 TALLOC_FREE(frame);
1017                 return NULL;
1018
1019         }
1020
1021         dirp = &context->internal->dirent;
1022         maxlen = sizeof(context->internal->_dirent_name);
1023
1024         smbc_readdir_internal(context, dirp, dirent, maxlen);
1025
1026         dir->dir_next = dir->dir_next->next;
1027
1028         TALLOC_FREE(frame);
1029         return dirp;
1030 }
1031
1032 /*
1033  * Routine to get directory entries
1034  */
1035
1036 int
1037 SMBC_getdents_ctx(SMBCCTX *context,
1038                   SMBCFILE *dir,
1039                   struct smbc_dirent *dirp,
1040                   int count)
1041 {
1042         int rem = count;
1043         int reqd;
1044         int maxlen;
1045         char *ndir = (char *)dirp;
1046         struct smbc_dir_list *dirlist;
1047         TALLOC_CTX *frame = talloc_stackframe();
1048
1049         /* Check that all is ok first ... */
1050
1051         if (!context || !context->internal->initialized) {
1052
1053                 errno = EINVAL;
1054                 TALLOC_FREE(frame);
1055                 return -1;
1056
1057         }
1058
1059         if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
1060
1061                 errno = EBADF;
1062                 TALLOC_FREE(frame);
1063                 return -1;
1064
1065         }
1066
1067         if (dir->file != False) { /* FIXME, should be dir, perhaps */
1068
1069                 errno = ENOTDIR;
1070                 TALLOC_FREE(frame);
1071                 return -1;
1072
1073         }
1074
1075         /*
1076          * Now, retrieve the number of entries that will fit in what was passed
1077          * We have to figure out if the info is in the list, or we need to
1078          * send a request to the server to get the info.
1079          */
1080
1081         while ((dirlist = dir->dir_next)) {
1082                 struct smbc_dirent *dirent;
1083                 struct smbc_dirent *currentEntry = (struct smbc_dirent *)ndir;
1084
1085                 if (!dirlist->dirent) {
1086
1087                         errno = ENOENT;  /* Bad error */
1088                         TALLOC_FREE(frame);
1089                         return -1;
1090
1091                 }
1092
1093                 /* Do urlencoding of next entry, if so selected */
1094                 dirent = &context->internal->dirent;
1095                 maxlen = sizeof(context->internal->_dirent_name);
1096                 smbc_readdir_internal(context, dirent,
1097                                       dirlist->dirent, maxlen);
1098
1099                 reqd = dirent->dirlen;
1100
1101                 if (rem < reqd) {
1102
1103                         if (rem < count) { /* We managed to copy something */
1104
1105                                 errno = 0;
1106                                 TALLOC_FREE(frame);
1107                                 return count - rem;
1108
1109                         }
1110                         else { /* Nothing copied ... */
1111
1112                                 errno = EINVAL;  /* Not enough space ... */
1113                                 TALLOC_FREE(frame);
1114                                 return -1;
1115
1116                         }
1117
1118                 }
1119
1120                 memcpy(currentEntry, dirent, reqd); /* Copy the data in ... */
1121
1122                 currentEntry->comment = &currentEntry->name[0] +
1123                                                 dirent->namelen + 1;
1124
1125                 ndir += reqd;
1126                 rem -= reqd;
1127
1128                 /* Try and align the struct for the next entry
1129                    on a valid pointer boundary by appending zeros */
1130                 while((rem > 0) && ((unsigned long long)ndir & (sizeof(void*) - 1))) {
1131                         *ndir = '\0';
1132                         rem--;
1133                         ndir++;
1134                         currentEntry->dirlen++;
1135                 }
1136
1137                 dir->dir_next = dirlist = dirlist -> next;
1138         }
1139
1140         TALLOC_FREE(frame);
1141
1142         if (rem == count)
1143                 return 0;
1144         else
1145                 return count - rem;
1146
1147 }
1148
1149 /*
1150  * Routine to create a directory ...
1151  */
1152
1153 int
1154 SMBC_mkdir_ctx(SMBCCTX *context,
1155                const char *fname,
1156                mode_t mode)
1157 {
1158         SMBCSRV *srv = NULL;
1159         char *server = NULL;
1160         char *share = NULL;
1161         char *user = NULL;
1162         char *password = NULL;
1163         char *workgroup = NULL;
1164         char *path = NULL;
1165         char *targetpath = NULL;
1166         uint16_t port = 0;
1167         struct cli_state *targetcli = NULL;
1168         TALLOC_CTX *frame = talloc_stackframe();
1169         NTSTATUS status;
1170
1171         if (!context || !context->internal->initialized) {
1172                 errno = EINVAL;
1173                 TALLOC_FREE(frame);
1174                 return -1;
1175         }
1176
1177         if (!fname) {
1178                 errno = EINVAL;
1179                 TALLOC_FREE(frame);
1180                 return -1;
1181         }
1182
1183         DEBUG(4, ("smbc_mkdir(%s)\n", fname));
1184
1185         if (SMBC_parse_path(frame,
1186                             context,
1187                             fname,
1188                             &workgroup,
1189                             &server,
1190                             &port,
1191                             &share,
1192                             &path,
1193                             &user,
1194                             &password,
1195                             NULL)) {
1196                 errno = EINVAL;
1197                 TALLOC_FREE(frame);
1198                 return -1;
1199         }
1200
1201         if (!user || user[0] == (char)0) {
1202                 user = talloc_strdup(frame, smbc_getUser(context));
1203                 if (!user) {
1204                         errno = ENOMEM;
1205                         TALLOC_FREE(frame);
1206                         return -1;
1207                 }
1208         }
1209
1210         srv = SMBC_server(frame, context, True,
1211                           server, port, share, &workgroup, &user, &password);
1212
1213         if (!srv) {
1214
1215                 TALLOC_FREE(frame);
1216                 return -1;  /* errno set by SMBC_server */
1217
1218         }
1219
1220         /*d_printf(">>>mkdir: resolving %s\n", path);*/
1221         status = cli_resolve_path(frame, "", context->internal->auth_info,
1222                                   srv->cli, path, &targetcli, &targetpath);
1223         if (!NT_STATUS_IS_OK(status)) {
1224                 d_printf("Could not resolve %s\n", path);
1225                 errno = ENOENT;
1226                 TALLOC_FREE(frame);
1227                 return -1;
1228         }
1229         /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
1230
1231         if (!NT_STATUS_IS_OK(cli_mkdir(targetcli, targetpath))) {
1232                 errno = SMBC_errno(context, targetcli);
1233                 TALLOC_FREE(frame);
1234                 return -1;
1235
1236         }
1237
1238         TALLOC_FREE(frame);
1239         return 0;
1240
1241 }
1242
1243 /*
1244  * Our list function simply checks to see if a directory is not empty
1245  */
1246
1247 static NTSTATUS
1248 rmdir_list_fn(const char *mnt,
1249               struct file_info *finfo,
1250               const char *mask,
1251               void *state)
1252 {
1253         if (strncmp(finfo->name, ".", 1) != 0 &&
1254             strncmp(finfo->name, "..", 2) != 0) {
1255                 bool *smbc_rmdir_dirempty = (bool *)state;
1256                 *smbc_rmdir_dirempty = false;
1257         }
1258         return NT_STATUS_OK;
1259 }
1260
1261 /*
1262  * Routine to remove a directory
1263  */
1264
1265 int
1266 SMBC_rmdir_ctx(SMBCCTX *context,
1267                const char *fname)
1268 {
1269         SMBCSRV *srv = NULL;
1270         char *server = NULL;
1271         char *share = NULL;
1272         char *user = NULL;
1273         char *password = NULL;
1274         char *workgroup = NULL;
1275         char *path = NULL;
1276         char *targetpath = NULL;
1277         uint16_t port = 0;
1278         struct cli_state *targetcli = NULL;
1279         TALLOC_CTX *frame = talloc_stackframe();
1280         NTSTATUS status;
1281
1282         if (!context || !context->internal->initialized) {
1283                 errno = EINVAL;
1284                 TALLOC_FREE(frame);
1285                 return -1;
1286         }
1287
1288         if (!fname) {
1289                 errno = EINVAL;
1290                 TALLOC_FREE(frame);
1291                 return -1;
1292         }
1293
1294         DEBUG(4, ("smbc_rmdir(%s)\n", fname));
1295
1296         if (SMBC_parse_path(frame,
1297                             context,
1298                             fname,
1299                             &workgroup,
1300                             &server,
1301                             &port,
1302                             &share,
1303                             &path,
1304                             &user,
1305                             &password,
1306                             NULL)) {
1307                 errno = EINVAL;
1308                 TALLOC_FREE(frame);
1309                 return -1;
1310         }
1311
1312         if (!user || user[0] == (char)0) {
1313                 user = talloc_strdup(frame, smbc_getUser(context));
1314                 if (!user) {
1315                         errno = ENOMEM;
1316                         TALLOC_FREE(frame);
1317                         return -1;
1318                 }
1319         }
1320
1321         srv = SMBC_server(frame, context, True,
1322                           server, port, share, &workgroup, &user, &password);
1323
1324         if (!srv) {
1325
1326                 TALLOC_FREE(frame);
1327                 return -1;  /* errno set by SMBC_server */
1328
1329         }
1330
1331         /*d_printf(">>>rmdir: resolving %s\n", path);*/
1332         status = cli_resolve_path(frame, "", context->internal->auth_info,
1333                                   srv->cli, path, &targetcli, &targetpath);
1334         if (!NT_STATUS_IS_OK(status)) {
1335                 d_printf("Could not resolve %s\n", path);
1336                 errno = ENOENT;
1337                 TALLOC_FREE(frame);
1338                 return -1;
1339         }
1340         /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
1341
1342         if (!NT_STATUS_IS_OK(cli_rmdir(targetcli, targetpath))) {
1343
1344                 errno = SMBC_errno(context, targetcli);
1345
1346                 if (errno == EACCES) {  /* Check if the dir empty or not */
1347
1348                         /* Local storage to avoid buffer overflows */
1349                         char *lpath;
1350                         bool smbc_rmdir_dirempty = true;
1351
1352                         lpath = talloc_asprintf(frame, "%s\\*",
1353                                                 targetpath);
1354                         if (!lpath) {
1355                                 errno = ENOMEM;
1356                                 TALLOC_FREE(frame);
1357                                 return -1;
1358                         }
1359
1360                         status = cli_list(targetcli, lpath,
1361                                           FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
1362                                           rmdir_list_fn,
1363                                           &smbc_rmdir_dirempty);
1364
1365                         if (!NT_STATUS_IS_OK(status)) {
1366                                 /* Fix errno to ignore latest error ... */
1367                                 DEBUG(5, ("smbc_rmdir: "
1368                                           "cli_list returned an error: %d\n",
1369                                           SMBC_errno(context, targetcli)));
1370                                 errno = EACCES;
1371
1372                         }
1373
1374                         if (smbc_rmdir_dirempty)
1375                                 errno = EACCES;
1376                         else
1377                                 errno = ENOTEMPTY;
1378
1379                 }
1380
1381                 TALLOC_FREE(frame);
1382                 return -1;
1383
1384         }
1385
1386         TALLOC_FREE(frame);
1387         return 0;
1388
1389 }
1390
1391 /*
1392  * Routine to return the current directory position
1393  */
1394
1395 off_t
1396 SMBC_telldir_ctx(SMBCCTX *context,
1397                  SMBCFILE *dir)
1398 {
1399         TALLOC_CTX *frame = talloc_stackframe();
1400
1401         if (!context || !context->internal->initialized) {
1402
1403                 errno = EINVAL;
1404                 TALLOC_FREE(frame);
1405                 return -1;
1406
1407         }
1408
1409         if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
1410
1411                 errno = EBADF;
1412                 TALLOC_FREE(frame);
1413                 return -1;
1414
1415         }
1416
1417         if (dir->file != False) { /* FIXME, should be dir, perhaps */
1418
1419                 errno = ENOTDIR;
1420                 TALLOC_FREE(frame);
1421                 return -1;
1422
1423         }
1424
1425         /* See if we're already at the end. */
1426         if (dir->dir_next == NULL) {
1427                 /* We are. */
1428                 TALLOC_FREE(frame);
1429                 return -1;
1430         }
1431
1432         /*
1433          * We return the pointer here as the offset
1434          */
1435         TALLOC_FREE(frame);
1436         return (off_t)(long)dir->dir_next->dirent;
1437 }
1438
1439 /*
1440  * A routine to run down the list and see if the entry is OK
1441  */
1442
1443 static struct smbc_dir_list *
1444 check_dir_ent(struct smbc_dir_list *list,
1445               struct smbc_dirent *dirent)
1446 {
1447
1448         /* Run down the list looking for what we want */
1449
1450         if (dirent) {
1451
1452                 struct smbc_dir_list *tmp = list;
1453
1454                 while (tmp) {
1455
1456                         if (tmp->dirent == dirent)
1457                                 return tmp;
1458
1459                         tmp = tmp->next;
1460
1461                 }
1462
1463         }
1464
1465         return NULL;  /* Not found, or an error */
1466
1467 }
1468
1469
1470 /*
1471  * Routine to seek on a directory
1472  */
1473
1474 int
1475 SMBC_lseekdir_ctx(SMBCCTX *context,
1476                   SMBCFILE *dir,
1477                   off_t offset)
1478 {
1479         long int l_offset = offset;  /* Handle problems of size */
1480         struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
1481         struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
1482         TALLOC_CTX *frame = talloc_stackframe();
1483
1484         if (!context || !context->internal->initialized) {
1485
1486                 errno = EINVAL;
1487                 TALLOC_FREE(frame);
1488                 return -1;
1489
1490         }
1491
1492         if (dir->file != False) { /* FIXME, should be dir, perhaps */
1493
1494                 errno = ENOTDIR;
1495                 TALLOC_FREE(frame);
1496                 return -1;
1497
1498         }
1499
1500         /* Now, check what we were passed and see if it is OK ... */
1501
1502         if (dirent == NULL) {  /* Seek to the begining of the list */
1503
1504                 dir->dir_next = dir->dir_list;
1505                 TALLOC_FREE(frame);
1506                 return 0;
1507
1508         }
1509
1510         if (offset == -1) {     /* Seek to the end of the list */
1511                 dir->dir_next = NULL;
1512                 TALLOC_FREE(frame);
1513                 return 0;
1514         }
1515
1516         /* Now, run down the list and make sure that the entry is OK       */
1517         /* This may need to be changed if we change the format of the list */
1518
1519         if ((list_ent = check_dir_ent(dir->dir_list, dirent)) == NULL) {
1520                 errno = EINVAL;   /* Bad entry */
1521                 TALLOC_FREE(frame);
1522                 return -1;
1523         }
1524
1525         dir->dir_next = list_ent;
1526
1527         TALLOC_FREE(frame);
1528         return 0;
1529 }
1530
1531 /*
1532  * Routine to fstat a dir
1533  */
1534
1535 int
1536 SMBC_fstatdir_ctx(SMBCCTX *context,
1537                   SMBCFILE *dir,
1538                   struct stat *st)
1539 {
1540
1541         if (!context || !context->internal->initialized) {
1542
1543                 errno = EINVAL;
1544                 return -1;
1545         }
1546
1547         /* No code yet ... */
1548         return 0;
1549 }
1550
1551 int
1552 SMBC_chmod_ctx(SMBCCTX *context,
1553                const char *fname,
1554                mode_t newmode)
1555 {
1556         SMBCSRV *srv = NULL;
1557         char *server = NULL;
1558         char *share = NULL;
1559         char *user = NULL;
1560         char *password = NULL;
1561         char *workgroup = NULL;
1562         char *targetpath = NULL;
1563         struct cli_state *targetcli = NULL;
1564         char *path = NULL;
1565         uint16_t mode;
1566         uint16_t port = 0;
1567         TALLOC_CTX *frame = talloc_stackframe();
1568         NTSTATUS status;
1569
1570         if (!context || !context->internal->initialized) {
1571
1572                 errno = EINVAL;  /* Best I can think of ... */
1573                 TALLOC_FREE(frame);
1574                 return -1;
1575         }
1576
1577         if (!fname) {
1578                 errno = EINVAL;
1579                 TALLOC_FREE(frame);
1580                 return -1;
1581         }
1582
1583         DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, (unsigned int)newmode));
1584
1585         if (SMBC_parse_path(frame,
1586                             context,
1587                             fname,
1588                             &workgroup,
1589                             &server,
1590                             &port,
1591                             &share,
1592                             &path,
1593                             &user,
1594                             &password,
1595                             NULL)) {
1596                 errno = EINVAL;
1597                 TALLOC_FREE(frame);
1598                 return -1;
1599         }
1600
1601         if (!user || user[0] == (char)0) {
1602                 user = talloc_strdup(frame, smbc_getUser(context));
1603                 if (!user) {
1604                         errno = ENOMEM;
1605                         TALLOC_FREE(frame);
1606                         return -1;
1607                 }
1608         }
1609
1610         srv = SMBC_server(frame, context, True,
1611                           server, port, share, &workgroup, &user, &password);
1612
1613         if (!srv) {
1614                 TALLOC_FREE(frame);
1615                 return -1;  /* errno set by SMBC_server */
1616         }
1617         
1618         /*d_printf(">>>unlink: resolving %s\n", path);*/
1619         status = cli_resolve_path(frame, "", context->internal->auth_info,
1620                                   srv->cli, path, &targetcli, &targetpath);
1621         if (!NT_STATUS_IS_OK(status)) {
1622                 d_printf("Could not resolve %s\n", path);
1623                 errno = ENOENT;
1624                 TALLOC_FREE(frame);
1625                 return -1;
1626         }
1627
1628         mode = 0;
1629
1630         if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= FILE_ATTRIBUTE_READONLY;
1631         if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= FILE_ATTRIBUTE_ARCHIVE;
1632         if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= FILE_ATTRIBUTE_SYSTEM;
1633         if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= FILE_ATTRIBUTE_HIDDEN;
1634
1635         if (!NT_STATUS_IS_OK(cli_setatr(targetcli, targetpath, mode, 0))) {
1636                 errno = SMBC_errno(context, targetcli);
1637                 TALLOC_FREE(frame);
1638                 return -1;
1639         }
1640
1641         TALLOC_FREE(frame);
1642         return 0;
1643 }
1644
1645 int
1646 SMBC_utimes_ctx(SMBCCTX *context,
1647                 const char *fname,
1648                 struct timeval *tbuf)
1649 {
1650         SMBCSRV *srv = NULL;
1651         char *server = NULL;
1652         char *share = NULL;
1653         char *user = NULL;
1654         char *password = NULL;
1655         char *workgroup = NULL;
1656         char *path = NULL;
1657         time_t access_time;
1658         time_t write_time;
1659         uint16_t port = 0;
1660         TALLOC_CTX *frame = talloc_stackframe();
1661
1662         if (!context || !context->internal->initialized) {
1663
1664                 errno = EINVAL;  /* Best I can think of ... */
1665                 TALLOC_FREE(frame);
1666                 return -1;
1667         }
1668
1669         if (!fname) {
1670                 errno = EINVAL;
1671                 TALLOC_FREE(frame);
1672                 return -1;
1673         }
1674
1675         if (tbuf == NULL) {
1676                 access_time = write_time = time(NULL);
1677         } else {
1678                 access_time = tbuf[0].tv_sec;
1679                 write_time = tbuf[1].tv_sec;
1680         }
1681
1682         if (DEBUGLVL(4)) {
1683                 char *p;
1684                 char atimebuf[32];
1685                 char mtimebuf[32];
1686
1687                 strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
1688                 atimebuf[sizeof(atimebuf) - 1] = '\0';
1689                 if ((p = strchr(atimebuf, '\n')) != NULL) {
1690                         *p = '\0';
1691                 }
1692
1693                 strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
1694                 mtimebuf[sizeof(mtimebuf) - 1] = '\0';
1695                 if ((p = strchr(mtimebuf, '\n')) != NULL) {
1696                         *p = '\0';
1697                 }
1698
1699                 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
1700                         fname, atimebuf, mtimebuf);
1701         }
1702
1703         if (SMBC_parse_path(frame,
1704                             context,
1705                             fname,
1706                             &workgroup,
1707                             &server,
1708                             &port,
1709                             &share,
1710                             &path,
1711                             &user,
1712                             &password,
1713                             NULL)) {
1714                 errno = EINVAL;
1715                 TALLOC_FREE(frame);
1716                 return -1;
1717         }
1718
1719         if (!user || user[0] == (char)0) {
1720                 user = talloc_strdup(frame, smbc_getUser(context));
1721                 if (!user) {
1722                         errno = ENOMEM;
1723                         TALLOC_FREE(frame);
1724                         return -1;
1725                 }
1726         }
1727
1728         srv = SMBC_server(frame, context, True,
1729                           server, port, share, &workgroup, &user, &password);
1730
1731         if (!srv) {
1732                 TALLOC_FREE(frame);
1733                 return -1;      /* errno set by SMBC_server */
1734         }
1735
1736         if (!SMBC_setatr(context, srv, path,
1737                          0, access_time, write_time, 0, 0)) {
1738                 TALLOC_FREE(frame);
1739                 return -1;      /* errno set by SMBC_setatr */
1740         }
1741
1742         TALLOC_FREE(frame);
1743         return 0;
1744 }
1745
1746 /*
1747  * Routine to unlink() a file
1748  */
1749
1750 int
1751 SMBC_unlink_ctx(SMBCCTX *context,
1752                 const char *fname)
1753 {
1754         char *server = NULL;
1755         char *share = NULL;
1756         char *user = NULL;
1757         char *password = NULL;
1758         char *workgroup = NULL;
1759         char *path = NULL;
1760         char *targetpath = NULL;
1761         uint16_t port = 0;
1762         struct cli_state *targetcli = NULL;
1763         SMBCSRV *srv = NULL;
1764         TALLOC_CTX *frame = talloc_stackframe();
1765         NTSTATUS status;
1766
1767         if (!context || !context->internal->initialized) {
1768
1769                 errno = EINVAL;  /* Best I can think of ... */
1770                 TALLOC_FREE(frame);
1771                 return -1;
1772
1773         }
1774
1775         if (!fname) {
1776                 errno = EINVAL;
1777                 TALLOC_FREE(frame);
1778                 return -1;
1779
1780         }
1781
1782         if (SMBC_parse_path(frame,
1783                             context,
1784                             fname,
1785                             &workgroup,
1786                             &server,
1787                             &port,
1788                             &share,
1789                             &path,
1790                             &user,
1791                             &password,
1792                             NULL)) {
1793                 errno = EINVAL;
1794                 TALLOC_FREE(frame);
1795                 return -1;
1796         }
1797
1798         if (!user || user[0] == (char)0) {
1799                 user = talloc_strdup(frame, smbc_getUser(context));
1800                 if (!user) {
1801                         errno = ENOMEM;
1802                         TALLOC_FREE(frame);
1803                         return -1;
1804                 }
1805         }
1806
1807         srv = SMBC_server(frame, context, True,
1808                           server, port, share, &workgroup, &user, &password);
1809
1810         if (!srv) {
1811                 TALLOC_FREE(frame);
1812                 return -1;  /* SMBC_server sets errno */
1813
1814         }
1815
1816         /*d_printf(">>>unlink: resolving %s\n", path);*/
1817         status = cli_resolve_path(frame, "", context->internal->auth_info,
1818                                   srv->cli, path, &targetcli, &targetpath);
1819         if (!NT_STATUS_IS_OK(status)) {
1820                 d_printf("Could not resolve %s\n", path);
1821                 errno = ENOENT;
1822                 TALLOC_FREE(frame);
1823                 return -1;
1824         }
1825         /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1826
1827         if (!NT_STATUS_IS_OK(cli_unlink(targetcli, targetpath, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN))) {
1828
1829                 errno = SMBC_errno(context, targetcli);
1830
1831                 if (errno == EACCES) { /* Check if the file is a directory */
1832
1833                         int saverr = errno;
1834                         off_t size = 0;
1835                         uint16_t mode = 0;
1836                         struct timespec write_time_ts;
1837                         struct timespec access_time_ts;
1838                         struct timespec change_time_ts;
1839                         SMB_INO_T ino = 0;
1840
1841                         if (!SMBC_getatr(context, srv, path, &mode, &size,
1842                                          NULL,
1843                                          &access_time_ts,
1844                                          &write_time_ts,
1845                                          &change_time_ts,
1846                                          &ino)) {
1847
1848                                 /* Hmmm, bad error ... What? */
1849
1850                                 errno = SMBC_errno(context, targetcli);
1851                                 TALLOC_FREE(frame);
1852                                 return -1;
1853
1854                         }
1855                         else {
1856
1857                                 if (IS_DOS_DIR(mode))
1858                                         errno = EISDIR;
1859                                 else
1860                                         errno = saverr;  /* Restore this */
1861
1862                         }
1863                 }
1864
1865                 TALLOC_FREE(frame);
1866                 return -1;
1867
1868         }
1869
1870         TALLOC_FREE(frame);
1871         return 0;  /* Success ... */
1872
1873 }
1874
1875 /*
1876  * Routine to rename() a file
1877  */
1878
1879 int
1880 SMBC_rename_ctx(SMBCCTX *ocontext,
1881                 const char *oname,
1882                 SMBCCTX *ncontext,
1883                 const char *nname)
1884 {
1885         char *server1 = NULL;
1886         char *share1 = NULL;
1887         char *server2 = NULL;
1888         char *share2 = NULL;
1889         char *user1 = NULL;
1890         char *user2 = NULL;
1891         char *password1 = NULL;
1892         char *password2 = NULL;
1893         char *workgroup = NULL;
1894         char *path1 = NULL;
1895         char *path2 = NULL;
1896         char *targetpath1 = NULL;
1897         char *targetpath2 = NULL;
1898         struct cli_state *targetcli1 = NULL;
1899         struct cli_state *targetcli2 = NULL;
1900         SMBCSRV *srv = NULL;
1901         uint16_t port1 = 0;
1902         uint16_t port2 = 0;
1903         TALLOC_CTX *frame = talloc_stackframe();
1904         NTSTATUS status;
1905
1906         if (!ocontext || !ncontext ||
1907             !ocontext->internal->initialized ||
1908             !ncontext->internal->initialized) {
1909
1910                 errno = EINVAL;  /* Best I can think of ... */
1911                 TALLOC_FREE(frame);
1912                 return -1;
1913         }
1914
1915         if (!oname || !nname) {
1916                 errno = EINVAL;
1917                 TALLOC_FREE(frame);
1918                 return -1;
1919         }
1920
1921         DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1922
1923         if (SMBC_parse_path(frame,
1924                             ocontext,
1925                             oname,
1926                             &workgroup,
1927                             &server1,
1928                             &port1,
1929                             &share1,
1930                             &path1,
1931                             &user1,
1932                             &password1,
1933                             NULL)) {
1934                 errno = EINVAL;
1935                 TALLOC_FREE(frame);
1936                 return -1;
1937         }
1938
1939         if (!user1 || user1[0] == (char)0) {
1940                 user1 = talloc_strdup(frame, smbc_getUser(ocontext));
1941                 if (!user1) {
1942                         errno = ENOMEM;
1943                         TALLOC_FREE(frame);
1944                         return -1;
1945                 }
1946         }
1947
1948         if (SMBC_parse_path(frame,
1949                             ncontext,
1950                             nname,
1951                             NULL,
1952                             &server2,
1953                             &port2,
1954                             &share2,
1955                             &path2,
1956                             &user2,
1957                             &password2,
1958                             NULL)) {
1959                 errno = EINVAL;
1960                 TALLOC_FREE(frame);
1961                 return -1;
1962         }
1963
1964         if (!user2 || user2[0] == (char)0) {
1965                 user2 = talloc_strdup(frame, smbc_getUser(ncontext));
1966                 if (!user2) {
1967                         errno = ENOMEM;
1968                         TALLOC_FREE(frame);
1969                         return -1;
1970                 }
1971         }
1972
1973         if (strcmp(server1, server2) || strcmp(share1, share2) ||
1974             strcmp(user1, user2)) {
1975                 /* Can't rename across file systems, or users?? */
1976                 errno = EXDEV;
1977                 TALLOC_FREE(frame);
1978                 return -1;
1979         }
1980
1981         srv = SMBC_server(frame, ocontext, True,
1982                           server1, port1, share1, &workgroup, &user1, &password1);
1983         if (!srv) {
1984                 TALLOC_FREE(frame);
1985                 return -1;
1986
1987         }
1988
1989         /* set the credentials to make DFS work */
1990         smbc_set_credentials_with_fallback(ocontext,
1991                                            workgroup,
1992                                            user1,
1993                                            password1);
1994
1995         /*d_printf(">>>rename: resolving %s\n", path1);*/
1996         status = cli_resolve_path(frame, "", ocontext->internal->auth_info,
1997                                   srv->cli, path1, &targetcli1, &targetpath1);
1998         if (!NT_STATUS_IS_OK(status)) {
1999                 d_printf("Could not resolve %s\n", path1);
2000                 errno = ENOENT;
2001                 TALLOC_FREE(frame);
2002                 return -1;
2003         }
2004         
2005         /* set the credentials to make DFS work */
2006         smbc_set_credentials_with_fallback(ncontext,
2007                                            workgroup,
2008                                            user2,
2009                                            password2);
2010         
2011         /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
2012         /*d_printf(">>>rename: resolving %s\n", path2);*/
2013         status = cli_resolve_path(frame, "", ncontext->internal->auth_info,
2014                                   srv->cli, path2, &targetcli2, &targetpath2);
2015         if (!NT_STATUS_IS_OK(status)) {
2016                 d_printf("Could not resolve %s\n", path2);
2017                 errno = ENOENT;
2018                 TALLOC_FREE(frame);
2019                 return -1;
2020         }
2021         /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
2022
2023         if (strcmp(smbXcli_conn_remote_name(targetcli1->conn), smbXcli_conn_remote_name(targetcli2->conn)) ||
2024             strcmp(targetcli1->share, targetcli2->share))
2025         {
2026                 /* can't rename across file systems */
2027                 errno = EXDEV;
2028                 TALLOC_FREE(frame);
2029                 return -1;
2030         }
2031
2032         if (!NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1, targetpath2))) {
2033                 int eno = SMBC_errno(ocontext, targetcli1);
2034
2035                 if (eno != EEXIST ||
2036                     !NT_STATUS_IS_OK(cli_unlink(targetcli1, targetpath2, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)) ||
2037                     !NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1, targetpath2))) {
2038
2039                         errno = eno;
2040                         TALLOC_FREE(frame);
2041                         return -1;
2042
2043                 }
2044         }
2045
2046         TALLOC_FREE(frame);
2047         return 0; /* Success */
2048 }
2049