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