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