source: trunk/samba/source/libsmb/libsmbclient.c @ 26

Last change on this file since 26 was 26, checked in by Paul Smedley, 14 years ago

Updated source to 3.0.25rc1

File size: 203.5 KB
Line 
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, 2004
9   
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14   
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19   
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23*/
24
25#include "includes.h"
26
27#include "include/libsmb_internal.h"
28
29struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir);
30struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
31                                         struct smbc_dirent *dirent);
32
33/*
34 * DOS Attribute values (used internally)
35 */
36typedef struct DOS_ATTR_DESC {
37        int mode;
38        SMB_OFF_T size;
39        time_t create_time;
40        time_t access_time;
41        time_t write_time;
42        time_t change_time;
43        SMB_INO_T inode;
44} DOS_ATTR_DESC;
45
46
47/*
48 * Internal flags for extended attributes
49 */
50
51/* internal mode values */
52#define SMBC_XATTR_MODE_ADD          1
53#define SMBC_XATTR_MODE_REMOVE       2
54#define SMBC_XATTR_MODE_REMOVE_ALL   3
55#define SMBC_XATTR_MODE_SET          4
56#define SMBC_XATTR_MODE_CHOWN        5
57#define SMBC_XATTR_MODE_CHGRP        6
58
59#define CREATE_ACCESS_READ      READ_CONTROL_ACCESS
60
61/*We should test for this in configure ... */
62#ifndef ENOTSUP
63#define ENOTSUP EOPNOTSUPP
64#endif
65
66/*
67 * Functions exported by libsmb_cache.c that we need here
68 */
69int smbc_default_cache_functions(SMBCCTX *context);
70
71/*
72 * check if an element is part of the list.
73 * FIXME: Does not belong here ! 
74 * Can anyone put this in a macro in dlinklist.h ?
75 * -- Tom
76 */
77static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
78        if (!p || !list) return False;
79        do {
80                if (p == list) return True;
81                list = list->next;
82        } while (list);
83        return False;
84}
85
86/*
87 * Find an lsa pipe handle associated with a cli struct.
88 */
89static struct rpc_pipe_client *
90find_lsa_pipe_hnd(struct cli_state *ipc_cli)
91{
92        struct rpc_pipe_client *pipe_hnd;
93
94        for (pipe_hnd = ipc_cli->pipe_list;
95             pipe_hnd;
96             pipe_hnd = pipe_hnd->next) {
97           
98                if (pipe_hnd->pipe_idx == PI_LSARPC) {
99                        return pipe_hnd;
100                }
101        }
102
103        return NULL;
104}
105
106static int
107smbc_close_ctx(SMBCCTX *context,
108               SMBCFILE *file);
109static off_t
110smbc_lseek_ctx(SMBCCTX *context,
111               SMBCFILE *file,
112               off_t offset,
113               int whence);
114
115extern BOOL in_client;
116
117/*
118 * Is the logging working / configfile read ?
119 */
120static int smbc_initialized = 0;
121
122static int 
123hex2int( unsigned int _char )
124{
125    if ( _char >= 'A' && _char <='F')
126        return _char - 'A' + 10;
127    if ( _char >= 'a' && _char <='f')
128        return _char - 'a' + 10;
129    if ( _char >= '0' && _char <='9')
130        return _char - '0';
131    return -1;
132}
133
134/*
135 * smbc_urldecode()
136 *
137 * Convert strings of %xx to their single character equivalent.  Each 'x' must
138 * be a valid hexadecimal digit, or that % sequence is left undecoded.
139 *
140 * dest may, but need not be, the same pointer as src.
141 *
142 * Returns the number of % sequences which could not be converted due to lack
143 * of two following hexadecimal digits.
144 */
145int
146smbc_urldecode(char *dest, char * src, size_t max_dest_len)
147{
148        int old_length = strlen(src);
149        int i = 0;
150        int err_count = 0;
151        pstring temp;
152        char * p;
153
154        if ( old_length == 0 ) {
155                return 0;
156        }
157
158        p = temp;
159        while ( i < old_length ) {
160                unsigned char character = src[ i++ ];
161
162                if (character == '%') {
163                        int a = i+1 < old_length ? hex2int( src[i] ) : -1;
164                        int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
165
166                        /* Replace valid sequence */
167                        if (a != -1 && b != -1) {
168
169                                /* Replace valid %xx sequence with %dd */
170                                character = (a * 16) + b;
171
172                                if (character == '\0') {
173                                        break; /* Stop at %00 */
174                                }
175
176                                i += 2;
177                        } else {
178
179                                err_count++;
180                        }
181                }
182
183                *p++ = character;
184        }
185
186        *p = '\0';
187
188        strncpy(dest, temp, max_dest_len - 1);
189        dest[max_dest_len - 1] = '\0';
190
191        return err_count;
192}
193
194/*
195 * smbc_urlencode()
196 *
197 * Convert any characters not specifically allowed in a URL into their %xx
198 * equivalent.
199 *
200 * Returns the remaining buffer length.
201 */
202int
203smbc_urlencode(char * dest, char * src, int max_dest_len)
204{
205        char hex[] = "0123456789ABCDEF";
206
207        for (; *src != '\0' && max_dest_len >= 3; src++) {
208
209                if ((*src < '0' &&
210                     *src != '-' &&
211                     *src != '.') ||
212                    (*src > '9' &&
213                     *src < 'A') ||
214                    (*src > 'Z' &&
215                     *src < 'a' &&
216                     *src != '_') ||
217                    (*src > 'z')) {
218                        *dest++ = '%';
219                        *dest++ = hex[(*src >> 4) & 0x0f];
220                        *dest++ = hex[*src & 0x0f];
221                        max_dest_len -= 3;
222                } else {
223                        *dest++ = *src;
224                        max_dest_len--;
225                }
226        }
227
228        *dest++ = '\0';
229        max_dest_len--;
230       
231        return max_dest_len;
232}
233
234/*
235 * Function to parse a path and turn it into components
236 *
237 * The general format of an SMB URI is explain in Christopher Hertel's CIFS
238 * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
239 * general format ("smb:" only; we do not look for "cifs:").
240 *
241 *
242 * We accept:
243 *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
244 *
245 * Meaning of URLs:
246 *
247 * smb://           Show all workgroups.
248 *
249 *                  The method of locating the list of workgroups varies
250 *                  depending upon the setting of the context variable
251 *                  context->options.browse_max_lmb_count.  This value
252 *                  determine the maximum number of local master browsers to
253 *                  query for the list of workgroups.  In order to ensure that
254 *                  a complete list of workgroups is obtained, all master
255 *                  browsers must be queried, but if there are many
256 *                  workgroups, the time spent querying can begin to add up.
257 *                  For small networks (not many workgroups), it is suggested
258 *                  that this variable be set to 0, indicating query all local
259 *                  master browsers.  When the network has many workgroups, a
260 *                  reasonable setting for this variable might be around 3.
261 *
262 * smb://name/      if name<1D> or name<1B> exists, list servers in
263 *                  workgroup, else, if name<20> exists, list all shares
264 *                  for server ...
265 *
266 * If "options" are provided, this function returns the entire option list as a
267 * string, for later parsing by the caller.  Note that currently, no options
268 * are supported.
269 */
270
271static const char *smbc_prefix = "smb:";
272
273static int
274smbc_parse_path(SMBCCTX *context,
275                const char *fname,
276                char *workgroup, int workgroup_len,
277                char *server, int server_len,
278                char *share, int share_len,
279                char *path, int path_len,
280                char *user, int user_len,
281                char *password, int password_len,
282                char *options, int options_len)
283{
284        static pstring s;
285        pstring userinfo;
286        const char *p;
287        char *q, *r;
288        int len;
289
290        server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
291
292        /*
293         * Assume we wont find an authentication domain to parse, so default
294         * to the workgroup in the provided context.
295         */
296        if (workgroup != NULL) {
297                strncpy(workgroup, context->workgroup, workgroup_len - 1);
298                workgroup[workgroup_len - 1] = '\0';
299        }
300
301        if (options != NULL && options_len > 0) {
302                options[0] = (char)0;
303        }
304        pstrcpy(s, fname);
305
306        /* see if it has the right prefix */
307        len = strlen(smbc_prefix);
308        if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
309                return -1; /* What about no smb: ? */
310        }
311
312        p = s + len;
313
314        /* Watch the test below, we are testing to see if we should exit */
315
316        if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
317
318                DEBUG(1, ("Invalid path (does not begin with smb://"));
319                return -1;
320
321        }
322
323        p += 2;  /* Skip the double slash */
324
325        /* See if any options were specified */
326        if ((q = strrchr(p, '?')) != NULL ) {
327                /* There are options.  Null terminate here and point to them */
328                *q++ = '\0';
329               
330                DEBUG(4, ("Found options '%s'", q));
331
332                /* Copy the options */
333                if (options != NULL && options_len > 0) {
334                        safe_strcpy(options, q, options_len - 1);
335                }
336        }
337
338        if (*p == (char)0)
339            goto decoding;
340
341        if (*p == '/') {
342                int wl = strlen(context->workgroup);
343
344                if (wl > 16) {
345                        wl = 16;
346                }
347
348                strncpy(server, context->workgroup, wl);
349                server[wl] = '\0';
350                return 0;
351        }
352
353        /*
354         * ok, its for us. Now parse out the server, share etc.
355         *
356         * However, we want to parse out [[domain;]user[:password]@] if it
357         * exists ...
358         */
359
360        /* check that '@' occurs before '/', if '/' exists at all */
361        q = strchr_m(p, '@');
362        r = strchr_m(p, '/');
363        if (q && (!r || q < r)) {
364                pstring username, passwd, domain;
365                const char *u = userinfo;
366
367                next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
368
369                username[0] = passwd[0] = domain[0] = 0;
370
371                if (strchr_m(u, ';')) {
372     
373                        next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
374
375                }
376
377                if (strchr_m(u, ':')) {
378
379                        next_token_no_ltrim(&u, username, ":", sizeof(fstring));
380
381                        pstrcpy(passwd, u);
382
383                }
384                else {
385
386                        pstrcpy(username, u);
387
388                }
389
390                if (domain[0] && workgroup) {
391                        strncpy(workgroup, domain, workgroup_len - 1);
392                        workgroup[workgroup_len - 1] = '\0';
393                }
394
395                if (username[0]) {
396                        strncpy(user, username, user_len - 1);
397                        user[user_len - 1] = '\0';
398                }
399
400                if (passwd[0]) {
401                        strncpy(password, passwd, password_len - 1);
402                        password[password_len - 1] = '\0';
403                }
404
405        }
406
407        if (!next_token(&p, server, "/", sizeof(fstring))) {
408
409                return -1;
410
411        }
412
413        if (*p == (char)0) goto decoding;  /* That's it ... */
414 
415        if (!next_token(&p, share, "/", sizeof(fstring))) {
416
417                return -1;
418
419        }
420
421        /*
422         * Prepend a leading slash if there's a file path, as required by
423         * NetApp filers.
424         */
425        *path = '\0';
426        if (*p != '\0') {
427                *path = '/';
428                safe_strcpy(path + 1, p, path_len - 2);
429        }
430
431        all_string_sub(path, "/", "\\", 0);
432
433 decoding:
434        (void) smbc_urldecode(path, path, path_len);
435        (void) smbc_urldecode(server, server, server_len);
436        (void) smbc_urldecode(share, share, share_len);
437        (void) smbc_urldecode(user, user, user_len);
438        (void) smbc_urldecode(password, password, password_len);
439
440        return 0;
441}
442
443/*
444 * Verify that the options specified in a URL are valid
445 */
446static int
447smbc_check_options(char *server,
448                   char *share,
449                   char *path,
450                   char *options)
451{
452        DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
453                  "path='%s' options='%s'\n",
454                  server, share, path, options));
455
456        /* No options at all is always ok */
457        if (! *options) return 0;
458
459        /* Currently, we don't support any options. */
460        return -1;
461}
462
463/*
464 * Convert an SMB error into a UNIX error ...
465 */
466static int
467smbc_errno(SMBCCTX *context,
468           struct cli_state *c)
469{
470        int ret = cli_errno(c);
471       
472        if (cli_is_dos_error(c)) {
473                uint8 eclass;
474                uint32 ecode;
475
476                cli_dos_error(c, &eclass, &ecode);
477               
478                DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", 
479                         (int)eclass, (int)ecode, (int)ecode, ret));
480        } else {
481                NTSTATUS status;
482
483                status = cli_nt_error(c);
484
485                DEBUG(3,("smbc errno %s -> %d\n",
486                         nt_errstr(status), ret));
487        }
488
489        return ret;
490}
491
492/*
493 * Check a server for being alive and well.
494 * returns 0 if the server is in shape. Returns 1 on error
495 *
496 * Also useable outside libsmbclient to enable external cache
497 * to do some checks too.
498 */
499static int
500smbc_check_server(SMBCCTX * context,
501                  SMBCSRV * server) 
502{
503        size_t size;
504        struct sockaddr addr;
505
506        /*
507         * Although the use of port 139 is not a guarantee that we're using
508         * netbios, we assume so.  We don't want to send a keepalive packet if
509         * not netbios because it's not valid, and Vista, at least,
510         * disconnects the client on such a request.
511         */
512        if (server->cli->port == 139) {
513                /* Assuming netbios.  Send a keepalive packet */
514                if ( send_keepalive(server->cli->fd) == False ) {
515                        return 1;
516                }
517        } else {
518                /*
519                 * Assuming not netbios.  Try a different method to detect if
520                 * the connection is still alive.
521                 */
522                size = sizeof(addr);
523                if (getpeername(server->cli->fd, &addr, &size) == -1) {
524                        return 1;
525                }
526        }
527
528        /* connection is ok */
529        return 0;
530}
531
532/*
533 * Remove a server from the cached server list it's unused.
534 * On success, 0 is returned. 1 is returned if the server could not be removed.
535 *
536 * Also useable outside libsmbclient
537 */
538int
539smbc_remove_unused_server(SMBCCTX * context,
540                          SMBCSRV * srv)
541{
542        SMBCFILE * file;
543
544        /* are we being fooled ? */
545        if (!context || !context->internal ||
546            !context->internal->_initialized || !srv) return 1;
547
548       
549        /* Check all open files/directories for a relation with this server */
550        for (file = context->internal->_files; file; file=file->next) {
551                if (file->srv == srv) {
552                        /* Still used */
553                        DEBUG(3, ("smbc_remove_usused_server: "
554                                  "%p still used by %p.\n", 
555                                  srv, file));
556                        return 1;
557                }
558        }
559
560        DLIST_REMOVE(context->internal->_servers, srv);
561
562        cli_shutdown(srv->cli);
563        srv->cli = NULL;
564
565        DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
566
567        context->callbacks.remove_cached_srv_fn(context, srv);
568
569        SAFE_FREE(srv);
570       
571        return 0;
572}
573
574static SMBCSRV *
575find_server(SMBCCTX *context,
576            const char *server,
577            const char *share,
578            fstring workgroup,
579            fstring username,
580            fstring password)
581{
582        SMBCSRV *srv;
583        int auth_called = 0;
584       
585 check_server_cache:
586
587        srv = context->callbacks.get_cached_srv_fn(context, server, share, 
588                                                   workgroup, username);
589
590        if (!auth_called && !srv && (!username[0] || !password[0])) {
591                if (context->internal->_auth_fn_with_context != NULL) {
592                         context->internal->_auth_fn_with_context(
593                                context,
594                                server, share,
595                                workgroup, sizeof(fstring),
596                                username, sizeof(fstring),
597                                password, sizeof(fstring));
598                } else {
599                        context->callbacks.auth_fn(
600                                server, share,
601                                workgroup, sizeof(fstring),
602                                username, sizeof(fstring),
603                                password, sizeof(fstring));
604                }
605
606                /*
607                 * However, smbc_auth_fn may have picked up info relating to
608                 * an existing connection, so try for an existing connection
609                 * again ...
610                 */
611                auth_called = 1;
612                goto check_server_cache;
613               
614        }
615       
616        if (srv) {
617                if (context->callbacks.check_server_fn(context, srv)) {
618                        /*
619                         * This server is no good anymore
620                         * Try to remove it and check for more possible
621                         * servers in the cache
622                         */
623                        if (context->callbacks.remove_unused_server_fn(context,
624                                                                       srv)) { 
625                                /*
626                                 * We could not remove the server completely,
627                                 * remove it from the cache so we will not get
628                                 * it again. It will be removed when the last
629                                 * file/dir is closed.
630                                 */
631                                context->callbacks.remove_cached_srv_fn(context,
632                                                                        srv);
633                        }
634                       
635                        /*
636                         * Maybe there are more cached connections to this
637                         * server
638                         */
639                        goto check_server_cache; 
640                }
641
642                return srv;
643        }
644
645        return NULL;
646}
647
648/*
649 * Connect to a server, possibly on an existing connection
650 *
651 * Here, what we want to do is: If the server and username
652 * match an existing connection, reuse that, otherwise, establish a
653 * new connection.
654 *
655 * If we have to create a new connection, call the auth_fn to get the
656 * info we need, unless the username and password were passed in.
657 */
658
659static SMBCSRV *
660smbc_server(SMBCCTX *context,
661            BOOL connect_if_not_found,
662            const char *server,
663            const char *share, 
664            fstring workgroup,
665            fstring username, 
666            fstring password)
667{
668        SMBCSRV *srv=NULL;
669        struct cli_state *c;
670        struct nmb_name called, calling;
671        const char *server_n = server;
672        pstring ipenv;
673        struct in_addr ip;
674        int tried_reverse = 0;
675        int port_try_first;
676        int port_try_next;
677        const char *username_used;
678 
679        zero_ip(&ip);
680        ZERO_STRUCT(c);
681
682        if (server[0] == 0) {
683                errno = EPERM;
684                return NULL;
685        }
686
687        /* Look for a cached connection */
688        srv = find_server(context, server, share,
689                          workgroup, username, password);
690       
691        /*
692         * If we found a connection and we're only allowed one share per
693         * server...
694         */
695        if (srv && *share != '\0' && context->options.one_share_per_server) {
696
697                /*
698                 * ... then if there's no current connection to the share,
699                 * connect to it.  find_server(), or rather the function
700                 * pointed to by context->callbacks.get_cached_srv_fn which
701                 * was called by find_server(), will have issued a tree
702                 * disconnect if the requested share is not the same as the
703                 * one that was already connected.
704                 */
705                if (srv->cli->cnum == (uint16) -1) {
706                        /* Ensure we have accurate auth info */
707                        if (context->internal->_auth_fn_with_context != NULL) {
708                                context->internal->_auth_fn_with_context(
709                                        context,
710                                        server, share,
711                                        workgroup, sizeof(fstring),
712                                        username, sizeof(fstring),
713                                        password, sizeof(fstring));
714                        } else {
715                                context->callbacks.auth_fn(
716                                        server, share,
717                                        workgroup, sizeof(fstring),
718                                        username, sizeof(fstring),
719                                        password, sizeof(fstring));
720                        }
721
722                        if (! cli_send_tconX(srv->cli, share, "?????",
723                                             password, strlen(password)+1)) {
724                       
725                                errno = smbc_errno(context, srv->cli);
726                                cli_shutdown(srv->cli);
727                                srv->cli = NULL;
728                                context->callbacks.remove_cached_srv_fn(context,
729                                                                        srv);
730                                srv = NULL;
731                        }
732
733                        /*
734                         * Regenerate the dev value since it's based on both
735                         * server and share
736                         */
737                        if (srv) {
738                                srv->dev = (dev_t)(str_checksum(server) ^
739                                                   str_checksum(share));
740                        }
741                }
742        }
743       
744        /* If we have a connection... */
745        if (srv) {
746
747                /* ... then we're done here.  Give 'em what they came for. */
748                return srv;
749        }
750
751        /* If we're not asked to connect when a connection doesn't exist... */
752        if (! connect_if_not_found) {
753                /* ... then we're done here. */
754                return NULL;
755        }
756
757        make_nmb_name(&calling, context->netbios_name, 0x0);
758        make_nmb_name(&called , server, 0x20);
759
760        DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
761 
762        DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
763
764 again:
765        slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
766
767        zero_ip(&ip);
768
769        /* have to open a new connection */
770        if ((c = cli_initialise()) == NULL) {
771                errno = ENOMEM;
772                return NULL;
773        }
774
775        if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
776                c->use_kerberos = True;
777        }
778        if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
779                c->fallback_after_kerberos = True;
780        }
781
782        c->timeout = context->timeout;
783
784        /*
785         * Force use of port 139 for first try if share is $IPC, empty, or
786         * null, so browse lists can work
787         */
788        if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
789                port_try_first = 139;
790                port_try_next = 445;
791        } else {
792                port_try_first = 445;
793                port_try_next = 139;
794        }
795
796        c->port = port_try_first;
797
798        if (!cli_connect(c, server_n, &ip)) {
799
800                /* First connection attempt failed.  Try alternate port. */
801                c->port = port_try_next;
802
803                if (!cli_connect(c, server_n, &ip)) {
804                        cli_shutdown(c);
805                        errno = ETIMEDOUT;
806                        return NULL;
807                }
808        }
809
810        if (!cli_session_request(c, &calling, &called)) {
811                cli_shutdown(c);
812                if (strcmp(called.name, "*SMBSERVER")) {
813                        make_nmb_name(&called , "*SMBSERVER", 0x20);
814                        goto again;
815                } else {  /* Try one more time, but ensure we don't loop */
816
817                        /* Only try this if server is an IP address ... */
818
819                        if (is_ipaddress(server) && !tried_reverse) {
820                                fstring remote_name;
821                                struct in_addr rem_ip;
822
823                                if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
824                                        DEBUG(4, ("Could not convert IP address "
825                                                "%s to struct in_addr\n", server));
826                                        errno = ETIMEDOUT;
827                                        return NULL;
828                                }
829
830                                tried_reverse++; /* Yuck */
831
832                                if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
833                                        make_nmb_name(&called, remote_name, 0x20);
834                                        goto again;
835                                }
836                        }
837                }
838                errno = ETIMEDOUT;
839                return NULL;
840        }
841 
842        DEBUG(4,(" session request ok\n"));
843 
844        if (!cli_negprot(c)) {
845                cli_shutdown(c);
846                errno = ETIMEDOUT;
847                return NULL;
848        }
849
850        username_used = username;
851
852        if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, 
853                                               password, strlen(password),
854                                               password, strlen(password),
855                                               workgroup))) {
856               
857                /* Failed.  Try an anonymous login, if allowed by flags. */
858                username_used = "";
859
860                if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
861                     !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
862                                                        password, 1,
863                                                        password, 0,
864                                                        workgroup))) {
865
866                        cli_shutdown(c);
867                        errno = EPERM;
868                        return NULL;
869                }
870        }
871
872        DEBUG(4,(" session setup ok\n"));
873
874        if (!cli_send_tconX(c, share, "?????",
875                            password, strlen(password)+1)) {
876                errno = smbc_errno(context, c);
877                cli_shutdown(c);
878                return NULL;
879        }
880 
881        DEBUG(4,(" tconx ok\n"));
882 
883        /*
884         * Ok, we have got a nice connection
885         * Let's allocate a server structure.
886         */
887
888        srv = SMB_MALLOC_P(SMBCSRV);
889        if (!srv) {
890                errno = ENOMEM;
891                goto failed;
892        }
893
894        ZERO_STRUCTP(srv);
895        srv->cli = c;
896        srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
897        srv->no_pathinfo = False;
898        srv->no_pathinfo2 = False;
899        srv->no_nt_session = False;
900
901        /* now add it to the cache (internal or external)  */
902        /* Let the cache function set errno if it wants to */
903        errno = 0;
904        if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
905                int saved_errno = errno;
906                DEBUG(3, (" Failed to add server to cache\n"));
907                errno = saved_errno;
908                if (errno == 0) {
909                        errno = ENOMEM;
910                }
911                goto failed;
912        }
913       
914        DEBUG(2, ("Server connect ok: //%s/%s: %p\n", 
915                  server, share, srv));
916
917        DLIST_ADD(context->internal->_servers, srv);
918        return srv;
919
920 failed:
921        cli_shutdown(c);
922        if (!srv) {
923                return NULL;
924        }
925 
926        SAFE_FREE(srv);
927        return NULL;
928}
929
930/*
931 * Connect to a server for getting/setting attributes, possibly on an existing
932 * connection.  This works similarly to smbc_server().
933 */
934static SMBCSRV *
935smbc_attr_server(SMBCCTX *context,
936                 const char *server,
937                 const char *share, 
938                 fstring workgroup,
939                 fstring username,
940                 fstring password,
941                 POLICY_HND *pol)
942{
943        int flags;
944        struct in_addr ip;
945        struct cli_state *ipc_cli;
946        struct rpc_pipe_client *pipe_hnd;
947        NTSTATUS nt_status;
948        SMBCSRV *ipc_srv=NULL;
949
950        /*
951         * See if we've already created this special connection.  Reference
952         * our "special" share name '*IPC$', which is an impossible real share
953         * name due to the leading asterisk.
954         */
955        ipc_srv = find_server(context, server, "*IPC$",
956                              workgroup, username, password);
957        if (!ipc_srv) {
958
959                /* We didn't find a cached connection.  Get the password */
960                if (*password == '\0') {
961                        /* ... then retrieve it now. */
962                        if (context->internal->_auth_fn_with_context != NULL) {
963                                context->internal->_auth_fn_with_context(
964                                        context,
965                                        server, share,
966                                        workgroup, sizeof(fstring),
967                                        username, sizeof(fstring),
968                                        password, sizeof(fstring));
969                        } else {
970                                context->callbacks.auth_fn(
971                                        server, share,
972                                        workgroup, sizeof(fstring),
973                                        username, sizeof(fstring),
974                                        password, sizeof(fstring));
975                        }
976                }
977       
978                flags = 0;
979                if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
980                        flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
981                }
982
983                zero_ip(&ip);
984                nt_status = cli_full_connection(&ipc_cli,
985                                                global_myname(), server, 
986                                                &ip, 0, "IPC$", "?????", 
987                                                username, workgroup,
988                                                password, flags,
989                                                Undefined, NULL);
990                if (! NT_STATUS_IS_OK(nt_status)) {
991                        DEBUG(1,("cli_full_connection failed! (%s)\n",
992                                 nt_errstr(nt_status)));
993                        errno = ENOTSUP;
994                        return NULL;
995                }
996
997                ipc_srv = SMB_MALLOC_P(SMBCSRV);
998                if (!ipc_srv) {
999                        errno = ENOMEM;
1000                        cli_shutdown(ipc_cli);
1001                        return NULL;
1002                }
1003
1004                ZERO_STRUCTP(ipc_srv);
1005                ipc_srv->cli = ipc_cli;
1006
1007                if (pol) {
1008                        pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
1009                                                            PI_LSARPC,
1010                                                            &nt_status);
1011                        if (!pipe_hnd) {
1012                                DEBUG(1, ("cli_nt_session_open fail!\n"));
1013                                errno = ENOTSUP;
1014                                cli_shutdown(ipc_srv->cli);
1015                                free(ipc_srv);
1016                                return NULL;
1017                        }
1018
1019                        /*
1020                         * Some systems don't support
1021                         * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
1022                         * so we might as well do it too.
1023                         */
1024       
1025                        nt_status = rpccli_lsa_open_policy(
1026                                pipe_hnd,
1027                                ipc_srv->cli->mem_ctx,
1028                                True, 
1029                                GENERIC_EXECUTE_ACCESS,
1030                                pol);
1031       
1032                        if (!NT_STATUS_IS_OK(nt_status)) {
1033                                errno = smbc_errno(context, ipc_srv->cli);
1034                                cli_shutdown(ipc_srv->cli);
1035                                return NULL;
1036                        }
1037                }
1038
1039                /* now add it to the cache (internal or external) */
1040
1041                errno = 0;      /* let cache function set errno if it likes */
1042                if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
1043                                                         server,
1044                                                         "*IPC$",
1045                                                         workgroup,
1046                                                         username)) {
1047                        DEBUG(3, (" Failed to add server to cache\n"));
1048                        if (errno == 0) {
1049                                errno = ENOMEM;
1050                        }
1051                        cli_shutdown(ipc_srv->cli);
1052                        free(ipc_srv);
1053                        return NULL;
1054                }
1055
1056                DLIST_ADD(context->internal->_servers, ipc_srv);
1057        }
1058
1059        return ipc_srv;
1060}
1061
1062/*
1063 * Routine to open() a file ...
1064 */
1065
1066static SMBCFILE *
1067smbc_open_ctx(SMBCCTX *context,
1068              const char *fname,
1069              int flags,
1070              mode_t mode)
1071{
1072        fstring server, share, user, password, workgroup;
1073        pstring path;
1074        pstring targetpath;
1075        struct cli_state *targetcli;
1076        SMBCSRV *srv   = NULL;
1077        SMBCFILE *file = NULL;
1078        int fd;
1079
1080        if (!context || !context->internal ||
1081            !context->internal->_initialized) {
1082
1083                errno = EINVAL;  /* Best I can think of ... */
1084                return NULL;
1085
1086        }
1087
1088        if (!fname) {
1089
1090                errno = EINVAL;
1091                return NULL;
1092
1093        }
1094
1095        if (smbc_parse_path(context, fname,
1096                            workgroup, sizeof(workgroup),
1097                            server, sizeof(server),
1098                            share, sizeof(share),
1099                            path, sizeof(path),
1100                            user, sizeof(user),
1101                            password, sizeof(password),
1102                            NULL, 0)) {
1103                errno = EINVAL;
1104                return NULL;
1105        }
1106
1107        if (user[0] == (char)0) fstrcpy(user, context->user);
1108
1109        srv = smbc_server(context, True,
1110                          server, share, workgroup, user, password);
1111
1112        if (!srv) {
1113
1114                if (errno == EPERM) errno = EACCES;
1115                return NULL;  /* smbc_server sets errno */
1116   
1117        }
1118
1119        /* Hmmm, the test for a directory is suspect here ... FIXME */
1120
1121        if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
1122   
1123                fd = -1;
1124
1125        }
1126        else {
1127         
1128                file = SMB_MALLOC_P(SMBCFILE);
1129
1130                if (!file) {
1131
1132                        errno = ENOMEM;
1133                        return NULL;
1134
1135                }
1136
1137                ZERO_STRUCTP(file);
1138
1139                /*d_printf(">>>open: resolving %s\n", path);*/
1140                if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1141                {
1142                        d_printf("Could not resolve %s\n", path);
1143                        SAFE_FREE(file);
1144                        return NULL;
1145                }
1146                /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
1147               
1148                if ((fd = cli_open(targetcli, targetpath, flags,
1149                                   context->internal->_share_mode)) < 0) {
1150
1151                        /* Handle the error ... */
1152
1153                        SAFE_FREE(file);
1154                        errno = smbc_errno(context, targetcli);
1155                        return NULL;
1156
1157                }
1158
1159                /* Fill in file struct */
1160
1161                file->cli_fd  = fd;
1162                file->fname   = SMB_STRDUP(fname);
1163                file->srv     = srv;
1164                file->offset  = 0;
1165                file->file    = True;
1166
1167                DLIST_ADD(context->internal->_files, file);
1168
1169                /*
1170                 * If the file was opened in O_APPEND mode, all write
1171                 * operations should be appended to the file.  To do that,
1172                 * though, using this protocol, would require a getattrE()
1173                 * call for each and every write, to determine where the end
1174                 * of the file is. (There does not appear to be an append flag
1175                 * in the protocol.)  Rather than add all of that overhead of
1176                 * retrieving the current end-of-file offset prior to each
1177                 * write operation, we'll assume that most append operations
1178                 * will continuously write, so we'll just set the offset to
1179                 * the end of the file now and hope that's adequate.
1180                 *
1181                 * Note to self: If this proves inadequate, and O_APPEND
1182                 * should, in some cases, be forced for each write, add a
1183                 * field in the context options structure, for
1184                 * "strict_append_mode" which would select between the current
1185                 * behavior (if FALSE) or issuing a getattrE() prior to each
1186                 * write and forcing the write to the end of the file (if
1187                 * TRUE).  Adding that capability will likely require adding
1188                 * an "append" flag into the _SMBCFILE structure to track
1189                 * whether a file was opened in O_APPEND mode.  -- djl
1190                 */
1191                if (flags & O_APPEND) {
1192                        if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
1193                                (void) smbc_close_ctx(context, file);
1194                                errno = ENXIO;
1195                                return NULL;
1196                        }
1197                }
1198
1199                return file;
1200
1201        }
1202
1203        /* Check if opendir needed ... */
1204
1205        if (fd == -1) {
1206                int eno = 0;
1207
1208                eno = smbc_errno(context, srv->cli);
1209                file = context->opendir(context, fname);
1210                if (!file) errno = eno;
1211                return file;
1212
1213        }
1214
1215        errno = EINVAL; /* FIXME, correct errno ? */
1216        return NULL;
1217
1218}
1219
1220/*
1221 * Routine to create a file
1222 */
1223
1224static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
1225
1226static SMBCFILE *
1227smbc_creat_ctx(SMBCCTX *context,
1228               const char *path,
1229               mode_t mode)
1230{
1231
1232        if (!context || !context->internal ||
1233            !context->internal->_initialized) {
1234
1235                errno = EINVAL;
1236                return NULL;
1237
1238        }
1239
1240        return smbc_open_ctx(context, path, creat_bits, mode);
1241}
1242
1243/*
1244 * Routine to read() a file ...
1245 */
1246
1247static ssize_t
1248smbc_read_ctx(SMBCCTX *context,
1249              SMBCFILE *file,
1250              void *buf,
1251              size_t count)
1252{
1253        int ret;
1254        fstring server, share, user, password;
1255        pstring path, targetpath;
1256        struct cli_state *targetcli;
1257
1258        /*
1259         * offset:
1260         *
1261         * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
1262         * appears to pass file->offset (which is type off_t) differently than
1263         * a local variable of type off_t.  Using local variable "offset" in
1264         * the call to cli_read() instead of file->offset fixes a problem
1265         * retrieving data at an offset greater than 4GB.
1266         */
1267        off_t offset;
1268
1269        if (!context || !context->internal ||
1270            !context->internal->_initialized) {
1271
1272                errno = EINVAL;
1273                return -1;
1274
1275        }
1276
1277        DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1278
1279        if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1280
1281                errno = EBADF;
1282                return -1;
1283
1284        }
1285
1286        offset = file->offset;
1287
1288        /* Check that the buffer exists ... */
1289
1290        if (buf == NULL) {
1291
1292                errno = EINVAL;
1293                return -1;
1294
1295        }
1296
1297        /*d_printf(">>>read: parsing %s\n", file->fname);*/
1298        if (smbc_parse_path(context, file->fname,
1299                            NULL, 0,
1300                            server, sizeof(server),
1301                            share, sizeof(share),
1302                            path, sizeof(path),
1303                            user, sizeof(user),
1304                            password, sizeof(password),
1305                            NULL, 0)) {
1306                errno = EINVAL;
1307                return -1;
1308        }
1309       
1310        /*d_printf(">>>read: resolving %s\n", path);*/
1311        if (!cli_resolve_path("", file->srv->cli, path,
1312                              &targetcli, targetpath))
1313        {
1314                d_printf("Could not resolve %s\n", path);
1315                return -1;
1316        }
1317        /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
1318       
1319        ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
1320
1321        if (ret < 0) {
1322
1323                errno = smbc_errno(context, targetcli);
1324                return -1;
1325
1326        }
1327
1328        file->offset += ret;
1329
1330        DEBUG(4, ("  --> %d\n", ret));
1331
1332        return ret;  /* Success, ret bytes of data ... */
1333
1334}
1335
1336/*
1337 * Routine to write() a file ...
1338 */
1339
1340static ssize_t
1341smbc_write_ctx(SMBCCTX *context,
1342               SMBCFILE *file,
1343               void *buf,
1344               size_t count)
1345{
1346        int ret;
1347        off_t offset;
1348        fstring server, share, user, password;
1349        pstring path, targetpath;
1350        struct cli_state *targetcli;
1351
1352        /* First check all pointers before dereferencing them */
1353       
1354        if (!context || !context->internal ||
1355            !context->internal->_initialized) {
1356
1357                errno = EINVAL;
1358                return -1;
1359
1360        }
1361
1362        if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1363
1364                errno = EBADF;
1365                return -1;
1366   
1367        }
1368
1369        /* Check that the buffer exists ... */
1370
1371        if (buf == NULL) {
1372
1373                errno = EINVAL;
1374                return -1;
1375
1376        }
1377
1378        offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
1379
1380        /*d_printf(">>>write: parsing %s\n", file->fname);*/
1381        if (smbc_parse_path(context, file->fname,
1382                            NULL, 0,
1383                            server, sizeof(server),
1384                            share, sizeof(share),
1385                            path, sizeof(path),
1386                            user, sizeof(user),
1387                            password, sizeof(password),
1388                            NULL, 0)) {
1389                errno = EINVAL;
1390                return -1;
1391        }
1392       
1393        /*d_printf(">>>write: resolving %s\n", path);*/
1394        if (!cli_resolve_path("", file->srv->cli, path,
1395                              &targetcli, targetpath))
1396        {
1397                d_printf("Could not resolve %s\n", path);
1398                return -1;
1399        }
1400        /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
1401
1402
1403        ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
1404
1405        if (ret <= 0) {
1406
1407                errno = smbc_errno(context, targetcli);
1408                return -1;
1409
1410        }
1411
1412        file->offset += ret;
1413
1414        return ret;  /* Success, 0 bytes of data ... */
1415}
1416 
1417/*
1418 * Routine to close() a file ...
1419 */
1420
1421static int
1422smbc_close_ctx(SMBCCTX *context,
1423               SMBCFILE *file)
1424{
1425        SMBCSRV *srv; 
1426        fstring server, share, user, password;
1427        pstring path, targetpath;
1428        struct cli_state *targetcli;
1429
1430        if (!context || !context->internal ||
1431            !context->internal->_initialized) {
1432
1433                errno = EINVAL;
1434                return -1;
1435
1436        }
1437
1438        if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1439   
1440                errno = EBADF;
1441                return -1;
1442
1443        }
1444
1445        /* IS a dir ... */
1446        if (!file->file) {
1447               
1448                return context->closedir(context, file);
1449
1450        }
1451
1452        /*d_printf(">>>close: parsing %s\n", file->fname);*/
1453        if (smbc_parse_path(context, file->fname,
1454                            NULL, 0,
1455                            server, sizeof(server),
1456                            share, sizeof(share),
1457                            path, sizeof(path),
1458                            user, sizeof(user),
1459                            password, sizeof(password),
1460                            NULL, 0)) {
1461                errno = EINVAL;
1462                return -1;
1463        }
1464       
1465        /*d_printf(">>>close: resolving %s\n", path);*/
1466        if (!cli_resolve_path("", file->srv->cli, path,
1467                              &targetcli, targetpath))
1468        {
1469                d_printf("Could not resolve %s\n", path);
1470                return -1;
1471        }
1472        /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
1473
1474        if (!cli_close(targetcli, file->cli_fd)) {
1475
1476                DEBUG(3, ("cli_close failed on %s. purging server.\n", 
1477                          file->fname));
1478                /* Deallocate slot and remove the server
1479                 * from the server cache if unused */
1480                errno = smbc_errno(context, targetcli);
1481                srv = file->srv;
1482                DLIST_REMOVE(context->internal->_files, file);
1483                SAFE_FREE(file->fname);
1484                SAFE_FREE(file);
1485                context->callbacks.remove_unused_server_fn(context, srv);
1486
1487                return -1;
1488
1489        }
1490
1491        DLIST_REMOVE(context->internal->_files, file);
1492        SAFE_FREE(file->fname);
1493        SAFE_FREE(file);
1494
1495        return 0;
1496}
1497
1498/*
1499 * Get info from an SMB server on a file. Use a qpathinfo call first
1500 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1501 */
1502static BOOL
1503smbc_getatr(SMBCCTX * context,
1504            SMBCSRV *srv,
1505            char *path, 
1506            uint16 *mode,
1507            SMB_OFF_T *size, 
1508            struct timespec *create_time_ts,
1509            struct timespec *access_time_ts,
1510            struct timespec *write_time_ts,
1511            struct timespec *change_time_ts,
1512            SMB_INO_T *ino)
1513{
1514        pstring fixedpath;
1515        pstring targetpath;
1516        struct cli_state *targetcli;
1517        time_t write_time;
1518
1519        if (!context || !context->internal ||
1520            !context->internal->_initialized) {
1521 
1522                errno = EINVAL;
1523                return -1;
1524 
1525        }
1526
1527        /* path fixup for . and .. */
1528        if (strequal(path, ".") || strequal(path, ".."))
1529                pstrcpy(fixedpath, "\\");
1530        else
1531        {
1532                pstrcpy(fixedpath, path);
1533                trim_string(fixedpath, NULL, "\\..");
1534                trim_string(fixedpath, NULL, "\\.");
1535        }
1536        DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1537 
1538        if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
1539        {
1540                d_printf("Couldn't resolve %s\n", path);
1541                return False;
1542        }
1543       
1544        if (!srv->no_pathinfo2 &&
1545            cli_qpathinfo2(targetcli, targetpath,
1546                           create_time_ts,
1547                           access_time_ts,
1548                           write_time_ts,
1549                           change_time_ts,
1550                           size, mode, ino)) {
1551            return True;
1552        }
1553
1554        /* if this is NT then don't bother with the getatr */
1555        if (targetcli->capabilities & CAP_NT_SMBS) {
1556                errno = EPERM;
1557                return False;
1558        }
1559
1560        if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
1561
1562                struct timespec w_time_ts;
1563
1564                w_time_ts = convert_time_t_to_timespec(write_time);
1565
1566                if (write_time_ts != NULL) {
1567                        *write_time_ts = w_time_ts;
1568                }
1569
1570                if (create_time_ts != NULL) {
1571                        *create_time_ts = w_time_ts;
1572                }
1573               
1574                if (access_time_ts != NULL) {
1575                        *access_time_ts = w_time_ts;
1576                }
1577               
1578                if (change_time_ts != NULL) {
1579                        *change_time_ts = w_time_ts;
1580                }
1581
1582                srv->no_pathinfo2 = True;
1583                return True;
1584        }
1585
1586        errno = EPERM;
1587        return False;
1588
1589}
1590
1591/*
1592 * Set file info on an SMB server.  Use setpathinfo call first.  If that
1593 * fails, use setattrE..
1594 *
1595 * Access and modification time parameters are always used and must be
1596 * provided.  Create time, if zero, will be determined from the actual create
1597 * time of the file.  If non-zero, the create time will be set as well.
1598 *
1599 * "mode" (attributes) parameter may be set to -1 if it is not to be set.
1600 */
1601static BOOL
1602smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
1603            time_t create_time,
1604            time_t access_time,
1605            time_t write_time,
1606            time_t change_time,
1607            uint16 mode)
1608{
1609        int fd;
1610        int ret;
1611
1612        /*
1613         * First, try setpathinfo (if qpathinfo succeeded), for it is the
1614         * modern function for "new code" to be using, and it works given a
1615         * filename rather than requiring that the file be opened to have its
1616         * attributes manipulated.
1617         */
1618        if (srv->no_pathinfo ||
1619            ! cli_setpathinfo(srv->cli, path,
1620                              create_time,
1621                              access_time,
1622                              write_time,
1623                              change_time,
1624                              mode)) {
1625
1626                /*
1627                 * setpathinfo is not supported; go to plan B.
1628                 *
1629                 * cli_setatr() does not work on win98, and it also doesn't
1630                 * support setting the access time (only the modification
1631                 * time), so in all cases, we open the specified file and use
1632                 * cli_setattrE() which should work on all OS versions, and
1633                 * supports both times.
1634                 */
1635
1636                /* Don't try {q,set}pathinfo() again, with this server */
1637                srv->no_pathinfo = True;
1638
1639                /* Open the file */
1640                if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
1641
1642                        errno = smbc_errno(context, srv->cli);
1643                        return -1;
1644                }
1645
1646                /* Set the new attributes */
1647                ret = cli_setattrE(srv->cli, fd,
1648                                   change_time,
1649                                   access_time,
1650                                   write_time);
1651
1652                /* Close the file */
1653                cli_close(srv->cli, fd);
1654
1655                /*
1656                 * Unfortunately, setattrE() doesn't have a provision for
1657                 * setting the access mode (attributes).  We'll have to try
1658                 * cli_setatr() for that, and with only this parameter, it
1659                 * seems to work on win98.
1660                 */
1661                if (ret && mode != (uint16) -1) {
1662                        ret = cli_setatr(srv->cli, path, mode, 0);
1663                }
1664
1665                if (! ret) {
1666                        errno = smbc_errno(context, srv->cli);
1667                        return False;
1668                }
1669        }
1670
1671        return True;
1672}
1673
1674 /*
1675  * Routine to unlink() a file
1676  */
1677
1678static int
1679smbc_unlink_ctx(SMBCCTX *context,
1680                const char *fname)
1681{
1682        fstring server, share, user, password, workgroup;
1683        pstring path, targetpath;
1684        struct cli_state *targetcli;
1685        SMBCSRV *srv = NULL;
1686
1687        if (!context || !context->internal ||
1688            !context->internal->_initialized) {
1689
1690                errno = EINVAL;  /* Best I can think of ... */
1691                return -1;
1692
1693        }
1694
1695        if (!fname) {
1696
1697                errno = EINVAL;
1698                return -1;
1699
1700        }
1701
1702        if (smbc_parse_path(context, fname,
1703                            workgroup, sizeof(workgroup),
1704                            server, sizeof(server),
1705                            share, sizeof(share),
1706                            path, sizeof(path),
1707                            user, sizeof(user),
1708                            password, sizeof(password),
1709                            NULL, 0)) {
1710                errno = EINVAL;
1711                return -1;
1712        }
1713
1714        if (user[0] == (char)0) fstrcpy(user, context->user);
1715
1716        srv = smbc_server(context, True,
1717                          server, share, workgroup, user, password);
1718
1719        if (!srv) {
1720
1721                return -1;  /* smbc_server sets errno */
1722
1723        }
1724
1725        /*d_printf(">>>unlink: resolving %s\n", path);*/
1726        if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1727        {
1728                d_printf("Could not resolve %s\n", path);
1729                return -1;
1730        }
1731        /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1732
1733        if (!cli_unlink(targetcli, targetpath)) {
1734
1735                errno = smbc_errno(context, targetcli);
1736
1737                if (errno == EACCES) { /* Check if the file is a directory */
1738
1739                        int saverr = errno;
1740                        SMB_OFF_T size = 0;
1741                        uint16 mode = 0;
1742                        struct timespec write_time_ts;
1743                        struct timespec access_time_ts;
1744                        struct timespec change_time_ts;
1745                        SMB_INO_T ino = 0;
1746
1747                        if (!smbc_getatr(context, srv, path, &mode, &size,
1748                                         NULL,
1749                                         &access_time_ts,
1750                                         &write_time_ts,
1751                                         &change_time_ts,
1752                                         &ino)) {
1753
1754                                /* Hmmm, bad error ... What? */
1755
1756                                errno = smbc_errno(context, targetcli);
1757                                return -1;
1758
1759                        }
1760                        else {
1761
1762                                if (IS_DOS_DIR(mode))
1763                                        errno = EISDIR;
1764                                else
1765                                        errno = saverr;  /* Restore this */
1766
1767                        }
1768                }
1769
1770                return -1;
1771
1772        }
1773
1774        return 0;  /* Success ... */
1775
1776}
1777
1778/*
1779 * Routine to rename() a file
1780 */
1781
1782static int
1783smbc_rename_ctx(SMBCCTX *ocontext,
1784                const char *oname, 
1785                SMBCCTX *ncontext,
1786                const char *nname)
1787{
1788        fstring server1;
1789        fstring share1;
1790        fstring server2;
1791        fstring share2;
1792        fstring user1;
1793        fstring user2;
1794        fstring password1;
1795        fstring password2;
1796        fstring workgroup;
1797        pstring path1;
1798        pstring path2;
1799        pstring targetpath1;
1800        pstring targetpath2;
1801        struct cli_state *targetcli1;
1802        struct cli_state *targetcli2;
1803        SMBCSRV *srv = NULL;
1804
1805        if (!ocontext || !ncontext || 
1806            !ocontext->internal || !ncontext->internal ||
1807            !ocontext->internal->_initialized || 
1808            !ncontext->internal->_initialized) {
1809
1810                errno = EINVAL;  /* Best I can think of ... */
1811                return -1;
1812
1813        }
1814       
1815        if (!oname || !nname) {
1816
1817                errno = EINVAL;
1818                return -1;
1819
1820        }
1821       
1822        DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1823
1824        smbc_parse_path(ocontext, oname,
1825                        workgroup, sizeof(workgroup),
1826                        server1, sizeof(server1),
1827                        share1, sizeof(share1),
1828                        path1, sizeof(path1),
1829                        user1, sizeof(user1),
1830                        password1, sizeof(password1),
1831                        NULL, 0);
1832
1833        if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
1834
1835        smbc_parse_path(ncontext, nname,
1836                        NULL, 0,
1837                        server2, sizeof(server2),
1838                        share2, sizeof(share2),
1839                        path2, sizeof(path2),
1840                        user2, sizeof(user2),
1841                        password2, sizeof(password2),
1842                        NULL, 0);
1843
1844        if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
1845
1846        if (strcmp(server1, server2) || strcmp(share1, share2) ||
1847            strcmp(user1, user2)) {
1848
1849                /* Can't rename across file systems, or users?? */
1850
1851                errno = EXDEV;
1852                return -1;
1853
1854        }
1855
1856        srv = smbc_server(ocontext, True,
1857                          server1, share1, workgroup, user1, password1);
1858        if (!srv) {
1859
1860                return -1;
1861
1862        }
1863
1864        /*d_printf(">>>rename: resolving %s\n", path1);*/
1865        if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
1866        {
1867                d_printf("Could not resolve %s\n", path1);
1868                return -1;
1869        }
1870        /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
1871        /*d_printf(">>>rename: resolving %s\n", path2);*/
1872        if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
1873        {
1874                d_printf("Could not resolve %s\n", path2);
1875                return -1;
1876        }
1877        /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
1878       
1879        if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
1880            strcmp(targetcli1->share, targetcli2->share))
1881        {
1882                /* can't rename across file systems */
1883               
1884                errno = EXDEV;
1885                return -1;
1886        }
1887
1888        if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
1889                int eno = smbc_errno(ocontext, targetcli1);
1890
1891                if (eno != EEXIST ||
1892                    !cli_unlink(targetcli1, targetpath2) ||
1893                    !cli_rename(targetcli1, targetpath1, targetpath2)) {
1894
1895                        errno = eno;
1896                        return -1;
1897
1898                }
1899        }
1900
1901        return 0; /* Success */
1902
1903}
1904
1905/*
1906 * A routine to lseek() a file
1907 */
1908
1909static off_t
1910smbc_lseek_ctx(SMBCCTX *context,
1911               SMBCFILE *file,
1912               off_t offset,
1913               int whence)
1914{
1915        SMB_OFF_T size;
1916        fstring server, share, user, password;
1917        pstring path, targetpath;
1918        struct cli_state *targetcli;
1919
1920        if (!context || !context->internal ||
1921            !context->internal->_initialized) {
1922
1923                errno = EINVAL;
1924                return -1;
1925               
1926        }
1927
1928        if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1929
1930                errno = EBADF;
1931                return -1;
1932
1933        }
1934
1935        if (!file->file) {
1936
1937                errno = EINVAL;
1938                return -1;      /* Can't lseek a dir ... */
1939
1940        }
1941
1942        switch (whence) {
1943        case SEEK_SET:
1944                file->offset = offset;
1945                break;
1946
1947        case SEEK_CUR:
1948                file->offset += offset;
1949                break;
1950
1951        case SEEK_END:
1952                /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
1953                if (smbc_parse_path(context, file->fname,
1954                                    NULL, 0,
1955                                    server, sizeof(server),
1956                                    share, sizeof(share),
1957                                    path, sizeof(path),
1958                                    user, sizeof(user),
1959                                    password, sizeof(password),
1960                                    NULL, 0)) {
1961                       
1962                                        errno = EINVAL;
1963                                        return -1;
1964                        }
1965               
1966                /*d_printf(">>>lseek: resolving %s\n", path);*/
1967                if (!cli_resolve_path("", file->srv->cli, path,
1968                                      &targetcli, targetpath))
1969                {
1970                        d_printf("Could not resolve %s\n", path);
1971                        return -1;
1972                }
1973                /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
1974               
1975                if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
1976                                   &size, NULL, NULL, NULL, NULL, NULL)) 
1977                {
1978                    SMB_OFF_T b_size = size;
1979                        if (!cli_getattrE(targetcli, file->cli_fd,
1980                                          NULL, &b_size, NULL, NULL, NULL)) 
1981                    {
1982                        errno = EINVAL;
1983                        return -1;
1984                    } else
1985                        size = b_size;
1986                }
1987                file->offset = size + offset;
1988                break;
1989
1990        default:
1991                errno = EINVAL;
1992                break;
1993
1994        }
1995
1996        return file->offset;
1997
1998}
1999
2000/*
2001 * Generate an inode number from file name for those things that need it
2002 */
2003
2004static ino_t
2005smbc_inode(SMBCCTX *context,
2006           const char *name)
2007{
2008
2009        if (!context || !context->internal ||
2010            !context->internal->_initialized) {
2011
2012                errno = EINVAL;
2013                return -1;
2014
2015        }
2016
2017        if (!*name) return 2; /* FIXME, why 2 ??? */
2018        return (ino_t)str_checksum(name);
2019
2020}
2021
2022/*
2023 * Routine to put basic stat info into a stat structure ... Used by stat and
2024 * fstat below.
2025 */
2026
2027static int
2028smbc_setup_stat(SMBCCTX *context,
2029                struct stat *st,
2030                char *fname,
2031                SMB_OFF_T size,
2032                int mode)
2033{
2034       
2035        st->st_mode = 0;
2036
2037        if (IS_DOS_DIR(mode)) {
2038                st->st_mode = SMBC_DIR_MODE;
2039        } else {
2040                st->st_mode = SMBC_FILE_MODE;
2041        }
2042
2043        if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
2044        if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
2045        if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
2046        if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
2047
2048        st->st_size = size;
2049#ifdef HAVE_STAT_ST_BLKSIZE
2050        st->st_blksize = 512;
2051#endif
2052#ifdef HAVE_STAT_ST_BLOCKS
2053        st->st_blocks = (size+511)/512;
2054#endif
2055        st->st_uid = getuid();
2056        st->st_gid = getgid();
2057
2058        if (IS_DOS_DIR(mode)) {
2059                st->st_nlink = 2;
2060        } else {
2061                st->st_nlink = 1;
2062        }
2063
2064        if (st->st_ino == 0) {
2065                st->st_ino = smbc_inode(context, fname);
2066        }
2067       
2068        return True;  /* FIXME: Is this needed ? */
2069
2070}
2071
2072/*
2073 * Routine to stat a file given a name
2074 */
2075
2076static int
2077smbc_stat_ctx(SMBCCTX *context,
2078              const char *fname,
2079              struct stat *st)
2080{
2081        SMBCSRV *srv;
2082        fstring server;
2083        fstring share;
2084        fstring user;
2085        fstring password;
2086        fstring workgroup;
2087        pstring path;
2088        struct timespec write_time_ts;
2089        struct timespec access_time_ts;
2090        struct timespec change_time_ts;
2091        SMB_OFF_T size = 0;
2092        uint16 mode = 0;
2093        SMB_INO_T ino = 0;
2094
2095        if (!context || !context->internal ||
2096            !context->internal->_initialized) {
2097
2098                errno = EINVAL;  /* Best I can think of ... */
2099                return -1;
2100   
2101        }
2102
2103        if (!fname) {
2104
2105                errno = EINVAL;
2106                return -1;
2107
2108        }
2109 
2110        DEBUG(4, ("smbc_stat(%s)\n", fname));
2111
2112        if (smbc_parse_path(context, fname,
2113                            workgroup, sizeof(workgroup),
2114                            server, sizeof(server),
2115                            share, sizeof(share),
2116                            path, sizeof(path),
2117                            user, sizeof(user),
2118                            password, sizeof(password),
2119                            NULL, 0)) {
2120                errno = EINVAL;
2121                return -1;
2122        }
2123
2124        if (user[0] == (char)0) fstrcpy(user, context->user);
2125
2126        srv = smbc_server(context, True,
2127                          server, share, workgroup, user, password);
2128
2129        if (!srv) {
2130                return -1;  /* errno set by smbc_server */
2131        }
2132
2133        if (!smbc_getatr(context, srv, path, &mode, &size, 
2134                         NULL,
2135                         &access_time_ts,
2136                         &write_time_ts,
2137                         &change_time_ts,
2138                         &ino)) {
2139
2140                errno = smbc_errno(context, srv->cli);
2141                return -1;
2142               
2143        }
2144
2145        st->st_ino = ino;
2146
2147        smbc_setup_stat(context, st, path, size, mode);
2148
2149        set_atimespec(st, access_time_ts);
2150        set_ctimespec(st, change_time_ts);
2151        set_mtimespec(st, write_time_ts);
2152        st->st_dev   = srv->dev;
2153
2154        return 0;
2155
2156}
2157
2158/*
2159 * Routine to stat a file given an fd
2160 */
2161
2162static int
2163smbc_fstat_ctx(SMBCCTX *context,
2164               SMBCFILE *file,
2165               struct stat *st)
2166{
2167        struct timespec change_time_ts;
2168        struct timespec access_time_ts;
2169        struct timespec write_time_ts;
2170        SMB_OFF_T size;
2171        uint16 mode;
2172        fstring server;
2173        fstring share;
2174        fstring user;
2175        fstring password;
2176        pstring path;
2177        pstring targetpath;
2178        struct cli_state *targetcli;
2179        SMB_INO_T ino = 0;
2180
2181        if (!context || !context->internal ||
2182            !context->internal->_initialized) {
2183
2184                errno = EINVAL;
2185                return -1;
2186
2187        }
2188
2189        if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2190
2191                errno = EBADF;
2192                return -1;
2193
2194        }
2195
2196        if (!file->file) {
2197
2198                return context->fstatdir(context, file, st);
2199
2200        }
2201
2202        /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2203        if (smbc_parse_path(context, file->fname,
2204                            NULL, 0,
2205                            server, sizeof(server),
2206                            share, sizeof(share),
2207                            path, sizeof(path),
2208                            user, sizeof(user),
2209                            password, sizeof(password),
2210                            NULL, 0)) {
2211                errno = EINVAL;
2212                return -1;
2213        }
2214       
2215        /*d_printf(">>>fstat: resolving %s\n", path);*/
2216        if (!cli_resolve_path("", file->srv->cli, path,
2217                              &targetcli, targetpath))
2218        {
2219                d_printf("Could not resolve %s\n", path);
2220                return -1;
2221        }
2222        /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2223
2224        if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
2225                           NULL,
2226                           &access_time_ts,
2227                           &write_time_ts,
2228                           &change_time_ts,
2229                           &ino)) {
2230
2231                time_t change_time, access_time, write_time;
2232
2233                if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
2234                                &change_time, &access_time, &write_time)) {
2235
2236                        errno = EINVAL;
2237                        return -1;
2238                }
2239
2240                change_time_ts = convert_time_t_to_timespec(change_time);
2241                access_time_ts = convert_time_t_to_timespec(access_time);
2242                write_time_ts = convert_time_t_to_timespec(write_time);
2243        }
2244
2245        st->st_ino = ino;
2246
2247        smbc_setup_stat(context, st, file->fname, size, mode);
2248
2249        set_atimespec(st, access_time_ts);
2250        set_ctimespec(st, change_time_ts);
2251        set_mtimespec(st, write_time_ts);
2252        st->st_dev = file->srv->dev;
2253
2254        return 0;
2255
2256}
2257
2258/*
2259 * Routine to open a directory
2260 * We accept the URL syntax explained in smbc_parse_path(), above.
2261 */
2262
2263static void
2264smbc_remove_dir(SMBCFILE *dir)
2265{
2266        struct smbc_dir_list *d,*f;
2267
2268        d = dir->dir_list;
2269        while (d) {
2270
2271                f = d; d = d->next;
2272
2273                SAFE_FREE(f->dirent);
2274                SAFE_FREE(f);
2275
2276        }
2277
2278        dir->dir_list = dir->dir_end = dir->dir_next = NULL;
2279
2280}
2281
2282static int
2283add_dirent(SMBCFILE *dir,
2284           const char *name,
2285           const char *comment,
2286           uint32 type)
2287{
2288        struct smbc_dirent *dirent;
2289        int size;
2290        int name_length = (name == NULL ? 0 : strlen(name));
2291        int comment_len = (comment == NULL ? 0 : strlen(comment));
2292
2293        /*
2294         * Allocate space for the dirent, which must be increased by the
2295         * size of the name and the comment and 1 each for the null terminator.
2296         */
2297
2298        size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
2299   
2300        dirent = (struct smbc_dirent *)SMB_MALLOC(size);
2301
2302        if (!dirent) {
2303
2304                dir->dir_error = ENOMEM;
2305                return -1;
2306
2307        }
2308
2309        ZERO_STRUCTP(dirent);
2310
2311        if (dir->dir_list == NULL) {
2312
2313                dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
2314                if (!dir->dir_list) {
2315
2316                        SAFE_FREE(dirent);
2317                        dir->dir_error = ENOMEM;
2318                        return -1;
2319
2320                }
2321                ZERO_STRUCTP(dir->dir_list);
2322
2323                dir->dir_end = dir->dir_next = dir->dir_list;
2324        }
2325        else {
2326
2327                dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
2328               
2329                if (!dir->dir_end->next) {
2330                       
2331                        SAFE_FREE(dirent);
2332                        dir->dir_error = ENOMEM;
2333                        return -1;
2334
2335                }
2336                ZERO_STRUCTP(dir->dir_end->next);
2337
2338                dir->dir_end = dir->dir_end->next;
2339        }
2340
2341        dir->dir_end->next = NULL;
2342        dir->dir_end->dirent = dirent;
2343       
2344        dirent->smbc_type = type;
2345        dirent->namelen = name_length;
2346        dirent->commentlen = comment_len;
2347        dirent->dirlen = size;
2348 
2349        /*
2350         * dirent->namelen + 1 includes the null (no null termination needed)
2351         * Ditto for dirent->commentlen.
2352         * The space for the two null bytes was allocated.
2353         */
2354        strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
2355        dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
2356        strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
2357       
2358        return 0;
2359
2360}
2361
2362static void
2363list_unique_wg_fn(const char *name,
2364                  uint32 type,
2365                  const char *comment,
2366                  void *state)
2367{
2368        SMBCFILE *dir = (SMBCFILE *)state;
2369        struct smbc_dir_list *dir_list;
2370        struct smbc_dirent *dirent;
2371        int dirent_type;
2372        int do_remove = 0;
2373
2374        dirent_type = dir->dir_type;
2375
2376        if (add_dirent(dir, name, comment, dirent_type) < 0) {
2377
2378                /* An error occurred, what do we do? */
2379                /* FIXME: Add some code here */
2380        }
2381
2382        /* Point to the one just added */
2383        dirent = dir->dir_end->dirent;
2384
2385        /* See if this was a duplicate */
2386        for (dir_list = dir->dir_list;
2387             dir_list != dir->dir_end;
2388             dir_list = dir_list->next) {
2389                if (! do_remove &&
2390                    strcmp(dir_list->dirent->name, dirent->name) == 0) {
2391                        /* Duplicate.  End end of list need to be removed. */
2392                        do_remove = 1;
2393                }
2394
2395                if (do_remove && dir_list->next == dir->dir_end) {
2396                        /* Found the end of the list.  Remove it. */
2397                        dir->dir_end = dir_list;
2398                        free(dir_list->next);
2399                        free(dirent);
2400                        dir_list->next = NULL;
2401                        break;
2402                }
2403        }
2404}
2405
2406static void
2407list_fn(const char *name,
2408        uint32 type,
2409        const char *comment,
2410        void *state)
2411{
2412        SMBCFILE *dir = (SMBCFILE *)state;
2413        int dirent_type;
2414
2415        /*
2416         * We need to process the type a little ...
2417         *
2418         * Disk share     = 0x00000000
2419         * Print share    = 0x00000001
2420         * Comms share    = 0x00000002 (obsolete?)
2421         * IPC$ share     = 0x00000003
2422         *
2423         * administrative shares:
2424         * ADMIN$, IPC$, C$, D$, E$ ...  are type |= 0x80000000
2425         */
2426       
2427        if (dir->dir_type == SMBC_FILE_SHARE) {
2428               
2429                switch (type) {
2430                case 0 | 0x80000000:
2431                case 0:
2432                        dirent_type = SMBC_FILE_SHARE;
2433                        break;
2434
2435                case 1:
2436                        dirent_type = SMBC_PRINTER_SHARE;
2437                        break;
2438
2439                case 2:
2440                        dirent_type = SMBC_COMMS_SHARE;
2441                        break;
2442
2443                case 3 | 0x80000000:
2444                case 3:
2445                        dirent_type = SMBC_IPC_SHARE;
2446                        break;
2447
2448                default:
2449                        dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
2450                        break;
2451                }
2452        }
2453        else {
2454                dirent_type = dir->dir_type;
2455        }
2456
2457        if (add_dirent(dir, name, comment, dirent_type) < 0) {
2458
2459                /* An error occurred, what do we do? */
2460                /* FIXME: Add some code here */
2461
2462        }
2463}
2464
2465static void
2466dir_list_fn(const char *mnt,
2467            file_info *finfo,
2468            const char *mask,
2469            void *state)
2470{
2471
2472        if (add_dirent((SMBCFILE *)state, finfo->name, "", 
2473                       (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
2474
2475                /* Handle an error ... */
2476
2477                /* FIXME: Add some code ... */
2478
2479        } 
2480
2481}
2482
2483static int
2484net_share_enum_rpc(struct cli_state *cli,
2485                   void (*fn)(const char *name,
2486                              uint32 type,
2487                              const char *comment,
2488                              void *state),
2489                   void *state)
2490{
2491        int i;
2492        WERROR result;
2493        ENUM_HND enum_hnd;
2494        uint32 info_level = 1;
2495        uint32 preferred_len = 0xffffffff;
2496        uint32 type;
2497        SRV_SHARE_INFO_CTR ctr;
2498        fstring name = "";
2499        fstring comment = "";
2500        void *mem_ctx;
2501        struct rpc_pipe_client *pipe_hnd;
2502        NTSTATUS nt_status;
2503
2504        /* Open the server service pipe */
2505        pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
2506        if (!pipe_hnd) {
2507                DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
2508                return -1;
2509        }
2510
2511        /* Allocate a context for parsing and for the entries in "ctr" */
2512        mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
2513        if (mem_ctx == NULL) {
2514                DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
2515                cli_rpc_pipe_close(pipe_hnd);
2516                return -1; 
2517        }
2518
2519        /* Issue the NetShareEnum RPC call and retrieve the response */
2520        init_enum_hnd(&enum_hnd, 0);
2521        result = rpccli_srvsvc_net_share_enum(pipe_hnd,
2522                                              mem_ctx,
2523                                              info_level,
2524                                              &ctr,
2525                                              preferred_len,
2526                                              &enum_hnd);
2527
2528        /* Was it successful? */
2529        if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
2530                /*  Nope.  Go clean up. */
2531                goto done;
2532        }
2533
2534        /* For each returned entry... */
2535        for (i = 0; i < ctr.num_entries; i++) {
2536
2537                /* pull out the share name */
2538                rpcstr_pull_unistr2_fstring(
2539                        name, &ctr.share.info1[i].info_1_str.uni_netname);
2540
2541                /* pull out the share's comment */
2542                rpcstr_pull_unistr2_fstring(
2543                        comment, &ctr.share.info1[i].info_1_str.uni_remark);
2544
2545                /* Get the type value */
2546                type = ctr.share.info1[i].info_1.type;
2547
2548                /* Add this share to the list */
2549                (*fn)(name, type, comment, state);
2550        }
2551
2552done:
2553        /* Close the server service pipe */
2554        cli_rpc_pipe_close(pipe_hnd);
2555
2556        /* Free all memory which was allocated for this request */
2557        TALLOC_FREE(mem_ctx);
2558
2559        /* Tell 'em if it worked */
2560        return W_ERROR_IS_OK(result) ? 0 : -1;
2561}
2562
2563
2564
2565static SMBCFILE *
2566smbc_opendir_ctx(SMBCCTX *context,
2567                 const char *fname)
2568{
2569        int saved_errno;
2570        fstring server, share, user, password, options;
2571        pstring workgroup;
2572        pstring path;
2573        uint16 mode;
2574        char *p;
2575        SMBCSRV *srv  = NULL;
2576        SMBCFILE *dir = NULL;
2577        struct _smbc_callbacks *cb;
2578        struct in_addr rem_ip;
2579
2580        if (!context || !context->internal ||
2581            !context->internal->_initialized) {
2582                DEBUG(4, ("no valid context\n"));
2583                errno = EINVAL + 8192;
2584                return NULL;
2585
2586        }
2587
2588        if (!fname) {
2589                DEBUG(4, ("no valid fname\n"));
2590                errno = EINVAL + 8193;
2591                return NULL;
2592        }
2593
2594        if (smbc_parse_path(context, fname,
2595                            workgroup, sizeof(workgroup),
2596                            server, sizeof(server),
2597                            share, sizeof(share),
2598                            path, sizeof(path),
2599                            user, sizeof(user),
2600                            password, sizeof(password),
2601                            options, sizeof(options))) {
2602                DEBUG(4, ("no valid path\n"));
2603                errno = EINVAL + 8194;
2604                return NULL;
2605        }
2606
2607        DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2608                  "path='%s' options='%s'\n",
2609                  fname, server, share, path, options));
2610
2611        /* Ensure the options are valid */
2612        if (smbc_check_options(server, share, path, options)) {
2613                DEBUG(4, ("unacceptable options (%s)\n", options));
2614                errno = EINVAL + 8195;
2615                return NULL;
2616        }
2617
2618        if (user[0] == (char)0) fstrcpy(user, context->user);
2619
2620        dir = SMB_MALLOC_P(SMBCFILE);
2621
2622        if (!dir) {
2623
2624                errno = ENOMEM;
2625                return NULL;
2626
2627        }
2628
2629        ZERO_STRUCTP(dir);
2630
2631        dir->cli_fd   = 0;
2632        dir->fname    = SMB_STRDUP(fname);
2633        dir->srv      = NULL;
2634        dir->offset   = 0;
2635        dir->file     = False;
2636        dir->dir_list = dir->dir_next = dir->dir_end = NULL;
2637
2638        if (server[0] == (char)0) {
2639
2640                int i;
2641                int count;
2642                int max_lmb_count;
2643                struct ip_service *ip_list;
2644                struct ip_service server_addr;
2645                struct user_auth_info u_info;
2646                struct cli_state *cli;
2647
2648                if (share[0] != (char)0 || path[0] != (char)0) {
2649
2650                        errno = EINVAL + 8196;
2651                        if (dir) {
2652                                SAFE_FREE(dir->fname);
2653                                SAFE_FREE(dir);
2654                        }
2655                        return NULL;
2656                }
2657
2658                /* Determine how many local master browsers to query */
2659                max_lmb_count = (context->options.browse_max_lmb_count == 0
2660                                 ? INT_MAX
2661                                 : context->options.browse_max_lmb_count);
2662
2663                pstrcpy(u_info.username, user);
2664                pstrcpy(u_info.password, password);
2665
2666                /*
2667                 * We have server and share and path empty but options
2668                 * requesting that we scan all master browsers for their list
2669                 * of workgroups/domains.  This implies that we must first try
2670                 * broadcast queries to find all master browsers, and if that
2671                 * doesn't work, then try our other methods which return only
2672                 * a single master browser.
2673                 */
2674
2675                ip_list = NULL;
2676                if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
2677
2678                        SAFE_FREE(ip_list);
2679
2680                        if (!find_master_ip(workgroup, &server_addr.ip)) {
2681
2682                                if (dir) {
2683                                        SAFE_FREE(dir->fname);
2684                                        SAFE_FREE(dir);
2685                                }
2686                                errno = ENOENT;
2687                                return NULL;
2688                        }
2689
2690                        ip_list = &server_addr;
2691                        count = 1;
2692                }
2693
2694                for (i = 0; i < count && i < max_lmb_count; i++) {
2695                        DEBUG(99, ("Found master browser %d of %d: %s\n",
2696                                   i+1, MAX(count, max_lmb_count),
2697                                   inet_ntoa(ip_list[i].ip)));
2698                       
2699                        cli = get_ipc_connect_master_ip(&ip_list[i],
2700                                                        workgroup, &u_info);
2701                        /* cli == NULL is the master browser refused to talk or
2702                           could not be found */
2703                        if ( !cli )
2704                                continue;
2705
2706                        fstrcpy(server, cli->desthost);
2707                        cli_shutdown(cli);
2708
2709                        DEBUG(4, ("using workgroup %s %s\n",
2710                                  workgroup, server));
2711
2712                        /*
2713                         * For each returned master browser IP address, get a
2714                         * connection to IPC$ on the server if we do not
2715                         * already have one, and determine the
2716                         * workgroups/domains that it knows about.
2717                         */
2718               
2719                        srv = smbc_server(context, True, server, "IPC$",
2720                                          workgroup, user, password);
2721                        if (!srv) {
2722                                continue;
2723                        }
2724               
2725                        dir->srv = srv;
2726                        dir->dir_type = SMBC_WORKGROUP;
2727
2728                        /* Now, list the stuff ... */
2729                       
2730                        if (!cli_NetServerEnum(srv->cli,
2731                                               workgroup,
2732                                               SV_TYPE_DOMAIN_ENUM,
2733                                               list_unique_wg_fn,
2734                                               (void *)dir)) {
2735                                continue;
2736                        }
2737                }
2738
2739                SAFE_FREE(ip_list);
2740        } else { 
2741                /*
2742                 * Server not an empty string ... Check the rest and see what
2743                 * gives
2744                 */
2745                if (*share == '\0') {
2746                        if (*path != '\0') {
2747
2748                                /* Should not have empty share with path */
2749                                errno = EINVAL + 8197;
2750                                if (dir) {
2751                                        SAFE_FREE(dir->fname);
2752                                        SAFE_FREE(dir);
2753                                }
2754                                return NULL;
2755       
2756                        }
2757
2758                        /*
2759                         * We don't know if <server> is really a server name
2760                         * or is a workgroup/domain name.  If we already have
2761                         * a server structure for it, we'll use it.
2762                         * Otherwise, check to see if <server><1D>,
2763                         * <server><1B>, or <server><20> translates.  We check
2764                         * to see if <server> is an IP address first.
2765                         */
2766
2767                        /*
2768                         * See if we have an existing server.  Do not
2769                         * establish a connection if one does not already
2770                         * exist.
2771                         */
2772                        srv = smbc_server(context, False, server, "IPC$",
2773                                          workgroup, user, password);
2774
2775                        /*
2776                         * If no existing server and not an IP addr, look for
2777                         * LMB or DMB
2778                         */
2779                        if (!srv &&
2780                            !is_ipaddress(server) &&
2781                            (resolve_name(server, &rem_ip, 0x1d) ||   /* LMB */
2782                             resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
2783
2784                                fstring buserver;
2785
2786                                dir->dir_type = SMBC_SERVER;
2787
2788                                /*
2789                                 * Get the backup list ...
2790                                 */
2791                                if (!name_status_find(server, 0, 0,
2792                                                      rem_ip, buserver)) {
2793
2794                                        DEBUG(0, ("Could not get name of "
2795                                                  "local/domain master browser "
2796                                                  "for server %s\n", server));
2797                                        if (dir) {
2798                                                SAFE_FREE(dir->fname);
2799                                                SAFE_FREE(dir);
2800                                        }
2801                                        errno = EPERM;
2802                                        return NULL;
2803
2804                                }
2805
2806                                /*
2807                                 * Get a connection to IPC$ on the server if
2808                                 * we do not already have one
2809                                 */
2810                                srv = smbc_server(context, True,
2811                                                  buserver, "IPC$",
2812                                                  workgroup, user, password);
2813                                if (!srv) {
2814                                        DEBUG(0, ("got no contact to IPC$\n"));
2815                                        if (dir) {
2816                                                SAFE_FREE(dir->fname);
2817                                                SAFE_FREE(dir);
2818                                        }
2819                                        return NULL;
2820
2821                                }
2822
2823                                dir->srv = srv;
2824
2825                                /* Now, list the servers ... */
2826                                if (!cli_NetServerEnum(srv->cli, server,
2827                                                       0x0000FFFE, list_fn,
2828                                                       (void *)dir)) {
2829
2830                                        if (dir) {
2831                                                SAFE_FREE(dir->fname);
2832                                                SAFE_FREE(dir);
2833                                        }
2834                                        return NULL;
2835                                }
2836                        } else if (srv ||
2837                                   (resolve_name(server, &rem_ip, 0x20))) {
2838                               
2839                                /* If we hadn't found the server, get one now */
2840                                if (!srv) {
2841                                        srv = smbc_server(context, True,
2842                                                          server, "IPC$",
2843                                                          workgroup,
2844                                                          user, password);
2845                                }
2846
2847                                if (!srv) {
2848                                        if (dir) {
2849                                                SAFE_FREE(dir->fname);
2850                                                SAFE_FREE(dir);
2851                                        }
2852                                        return NULL;
2853
2854                                }
2855
2856                                dir->dir_type = SMBC_FILE_SHARE;
2857                                dir->srv = srv;
2858
2859                                /* List the shares ... */
2860
2861                                if (net_share_enum_rpc(
2862                                            srv->cli,
2863                                            list_fn,
2864                                            (void *) dir) < 0 &&
2865                                    cli_RNetShareEnum(
2866                                            srv->cli,
2867                                            list_fn, 
2868                                            (void *)dir) < 0) {
2869                                               
2870                                        errno = cli_errno(srv->cli);
2871                                        if (dir) {
2872                                                SAFE_FREE(dir->fname);
2873                                                SAFE_FREE(dir);
2874                                        }
2875                                        return NULL;
2876
2877                                }
2878                        } else {
2879                                /* Neither the workgroup nor server exists */
2880                                errno = ECONNREFUSED;   
2881                                if (dir) {
2882                                        SAFE_FREE(dir->fname);
2883                                        SAFE_FREE(dir);
2884                                }
2885                                return NULL;
2886                        }
2887
2888                }
2889                else {
2890                        /*
2891                         * The server and share are specified ... work from
2892                         * there ...
2893                         */
2894                        pstring targetpath;
2895                        struct cli_state *targetcli;
2896
2897                        /* We connect to the server and list the directory */
2898                        dir->dir_type = SMBC_FILE_SHARE;
2899
2900                        srv = smbc_server(context, True, server, share,
2901                                          workgroup, user, password);
2902
2903                        if (!srv) {
2904
2905                                if (dir) {
2906                                        SAFE_FREE(dir->fname);
2907                                        SAFE_FREE(dir);
2908                                }
2909                                return NULL;
2910
2911                        }
2912
2913                        dir->srv = srv;
2914
2915                        /* Now, list the files ... */
2916
2917                        p = path + strlen(path);
2918                        pstrcat(path, "\\*");
2919
2920                        if (!cli_resolve_path("", srv->cli, path,
2921                                              &targetcli, targetpath))
2922                        {
2923                                d_printf("Could not resolve %s\n", path);
2924                                if (dir) {
2925                                        SAFE_FREE(dir->fname);
2926                                        SAFE_FREE(dir);
2927                                }
2928                                return NULL;
2929                        }
2930                       
2931                        if (cli_list(targetcli, targetpath,
2932                                     aDIR | aSYSTEM | aHIDDEN,
2933                                     dir_list_fn, (void *)dir) < 0) {
2934
2935                                if (dir) {
2936                                        SAFE_FREE(dir->fname);
2937                                        SAFE_FREE(dir);
2938                                }
2939                                saved_errno = smbc_errno(context, targetcli);
2940
2941                                if (saved_errno == EINVAL) {
2942                                    /*
2943                                     * See if they asked to opendir something
2944                                     * other than a directory.  If so, the
2945                                     * converted error value we got would have
2946                                     * been EINVAL rather than ENOTDIR.
2947                                     */
2948                                    *p = '\0'; /* restore original path */
2949
2950                                    if (smbc_getatr(context, srv, path,
2951                                                    &mode, NULL,
2952                                                    NULL, NULL, NULL, NULL,
2953                                                    NULL) &&
2954                                        ! IS_DOS_DIR(mode)) {
2955
2956                                        /* It is.  Correct the error value */
2957                                        saved_errno = ENOTDIR;
2958                                    }
2959                                }
2960
2961                                /*
2962                                 * If there was an error and the server is no
2963                                 * good any more...
2964                                 */
2965                                cb = &context->callbacks;
2966                                if (cli_is_error(targetcli) &&
2967                                    cb->check_server_fn(context, srv)) {
2968
2969                                    /* ... then remove it. */
2970                                    if (cb->remove_unused_server_fn(context,
2971                                                                    srv)) { 
2972                                        /*
2973                                         * We could not remove the server
2974                                         * completely, remove it from the
2975                                         * cache so we will not get it
2976                                         * again. It will be removed when the
2977                                         * last file/dir is closed.
2978                                         */
2979                                        cb->remove_cached_srv_fn(context, srv);
2980                                    }
2981                                }
2982
2983                                errno = saved_errno;
2984                                return NULL;
2985                        }
2986                }
2987
2988        }
2989
2990        DLIST_ADD(context->internal->_files, dir);
2991        return dir;
2992
2993}
2994
2995/*
2996 * Routine to close a directory
2997 */
2998
2999static int
3000smbc_closedir_ctx(SMBCCTX *context,
3001                  SMBCFILE *dir)
3002{
3003
3004        if (!context || !context->internal ||
3005            !context->internal->_initialized) {
3006
3007                errno = EINVAL;
3008                return -1;
3009
3010        }
3011
3012        if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3013
3014                errno = EBADF;
3015                return -1;
3016   
3017        }
3018
3019        smbc_remove_dir(dir); /* Clean it up */
3020
3021        DLIST_REMOVE(context->internal->_files, dir);
3022
3023        if (dir) {
3024
3025                SAFE_FREE(dir->fname);
3026                SAFE_FREE(dir);    /* Free the space too */
3027        }
3028
3029        return 0;
3030
3031}
3032
3033static void
3034smbc_readdir_internal(SMBCCTX * context,
3035                      struct smbc_dirent *dest,
3036                      struct smbc_dirent *src,
3037                      int max_namebuf_len)
3038{
3039        if (context->options.urlencode_readdir_entries) {
3040
3041                /* url-encode the name.  get back remaining buffer space */
3042                max_namebuf_len =
3043                        smbc_urlencode(dest->name, src->name, max_namebuf_len);
3044
3045                /* We now know the name length */
3046                dest->namelen = strlen(dest->name);
3047
3048                /* Save the pointer to the beginning of the comment */
3049                dest->comment = dest->name + dest->namelen + 1;
3050
3051                /* Copy the comment */
3052                strncpy(dest->comment, src->comment, max_namebuf_len - 1);
3053                dest->comment[max_namebuf_len - 1] = '\0';
3054
3055                /* Save other fields */
3056                dest->smbc_type = src->smbc_type;
3057                dest->commentlen = strlen(dest->comment);
3058                dest->dirlen = ((dest->comment + dest->commentlen + 1) -
3059                                (char *) dest);
3060        } else {
3061
3062                /* No encoding.  Just copy the entry as is. */
3063                memcpy(dest, src, src->dirlen);
3064                dest->comment = (char *)(&dest->name + src->namelen + 1);
3065        }
3066       
3067}
3068
3069/*
3070 * Routine to get a directory entry
3071 */
3072
3073struct smbc_dirent *
3074smbc_readdir_ctx(SMBCCTX *context,
3075                 SMBCFILE *dir)
3076{
3077        int maxlen;
3078        struct smbc_dirent *dirp, *dirent;
3079
3080        /* Check that all is ok first ... */
3081
3082        if (!context || !context->internal ||
3083            !context->internal->_initialized) {
3084
3085                errno = EINVAL;
3086                DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
3087                return NULL;
3088
3089        }
3090
3091        if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3092
3093                errno = EBADF;
3094                DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
3095                return NULL;
3096
3097        }
3098
3099        if (dir->file != False) { /* FIXME, should be dir, perhaps */
3100
3101                errno = ENOTDIR;
3102                DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
3103                return NULL;
3104
3105        }
3106
3107        if (!dir->dir_next) {
3108                return NULL;
3109        }
3110
3111        dirent = dir->dir_next->dirent;
3112        if (!dirent) {
3113
3114                errno = ENOENT;
3115                return NULL;
3116
3117        }
3118
3119        dirp = (struct smbc_dirent *)context->internal->_dirent;
3120        maxlen = (sizeof(context->internal->_dirent) -
3121                  sizeof(struct smbc_dirent));
3122
3123        smbc_readdir_internal(context, dirp, dirent, maxlen);
3124
3125        dir->dir_next = dir->dir_next->next;
3126
3127        return dirp;
3128}
3129
3130/*
3131 * Routine to get directory entries
3132 */
3133
3134static int
3135smbc_getdents_ctx(SMBCCTX *context,
3136                  SMBCFILE *dir,
3137                  struct smbc_dirent *dirp,
3138                  int count)
3139{
3140        int rem = count;
3141        int reqd;
3142        int maxlen;
3143        char *ndir = (char *)dirp;
3144        struct smbc_dir_list *dirlist;
3145
3146        /* Check that all is ok first ... */
3147
3148        if (!context || !context->internal ||
3149            !context->internal->_initialized) {
3150
3151                errno = EINVAL;
3152                return -1;
3153
3154        }
3155
3156        if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3157
3158                errno = EBADF;
3159                return -1;
3160   
3161        }
3162
3163        if (dir->file != False) { /* FIXME, should be dir, perhaps */
3164
3165                errno = ENOTDIR;
3166                return -1;
3167
3168        }
3169
3170        /*
3171         * Now, retrieve the number of entries that will fit in what was passed
3172         * We have to figure out if the info is in the list, or we need to
3173         * send a request to the server to get the info.
3174         */
3175
3176        while ((dirlist = dir->dir_next)) {
3177                struct smbc_dirent *dirent;
3178
3179                if (!dirlist->dirent) {
3180
3181                        errno = ENOENT;  /* Bad error */
3182                        return -1;
3183
3184                }
3185
3186                /* Do urlencoding of next entry, if so selected */
3187                dirent = (struct smbc_dirent *)context->internal->_dirent;
3188                maxlen = (sizeof(context->internal->_dirent) -
3189                          sizeof(struct smbc_dirent));
3190                smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
3191
3192                reqd = dirent->dirlen;
3193
3194                if (rem < reqd) {
3195
3196                        if (rem < count) { /* We managed to copy something */
3197
3198                                errno = 0;
3199                                return count - rem;
3200
3201                        }
3202                        else { /* Nothing copied ... */
3203
3204                                errno = EINVAL;  /* Not enough space ... */
3205                                return -1;
3206
3207                        }
3208
3209                }
3210
3211                memcpy(ndir, dirent, reqd); /* Copy the data in ... */
3212   
3213                ((struct smbc_dirent *)ndir)->comment = 
3214                        (char *)(&((struct smbc_dirent *)ndir)->name +
3215                                 dirent->namelen +
3216                                 1);
3217
3218                ndir += reqd;
3219
3220                rem -= reqd;
3221
3222                dir->dir_next = dirlist = dirlist -> next;
3223        }
3224
3225        if (rem == count)
3226                return 0;
3227        else 
3228                return count - rem;
3229
3230}
3231
3232/*
3233 * Routine to create a directory ...
3234 */
3235
3236static int
3237smbc_mkdir_ctx(SMBCCTX *context,
3238               const char *fname,
3239               mode_t mode)
3240{
3241        SMBCSRV *srv;
3242        fstring server;
3243        fstring share;
3244        fstring user;
3245        fstring password;
3246        fstring workgroup;
3247        pstring path, targetpath;
3248        struct cli_state *targetcli;
3249
3250        if (!context || !context->internal || 
3251            !context->internal->_initialized) {
3252
3253                errno = EINVAL;
3254                return -1;
3255
3256        }
3257
3258        if (!fname) {
3259
3260                errno = EINVAL;
3261                return -1;
3262
3263        }
3264 
3265        DEBUG(4, ("smbc_mkdir(%s)\n", fname));
3266
3267        if (smbc_parse_path(context, fname,
3268                            workgroup, sizeof(workgroup),
3269                            server, sizeof(server),
3270                            share, sizeof(share),
3271                            path, sizeof(path),
3272                            user, sizeof(user),
3273                            password, sizeof(password),
3274                            NULL, 0)) {
3275                errno = EINVAL;
3276                return -1;
3277        }
3278
3279        if (user[0] == (char)0) fstrcpy(user, context->user);
3280
3281        srv = smbc_server(context, True,
3282                          server, share, workgroup, user, password);
3283
3284        if (!srv) {
3285
3286                return -1;  /* errno set by smbc_server */
3287
3288        }
3289
3290        /*d_printf(">>>mkdir: resolving %s\n", path);*/
3291        if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3292        {
3293                d_printf("Could not resolve %s\n", path);
3294                return -1;
3295        }
3296        /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3297
3298        if (!cli_mkdir(targetcli, targetpath)) {
3299
3300                errno = smbc_errno(context, targetcli);
3301                return -1;
3302
3303        } 
3304
3305        return 0;
3306
3307}
3308
3309/*
3310 * Our list function simply checks to see if a directory is not empty
3311 */
3312
3313static int smbc_rmdir_dirempty = True;
3314
3315static void
3316rmdir_list_fn(const char *mnt,
3317              file_info *finfo,
3318              const char *mask,
3319              void *state)
3320{
3321        if (strncmp(finfo->name, ".", 1) != 0 &&
3322            strncmp(finfo->name, "..", 2) != 0) {
3323               
3324                smbc_rmdir_dirempty = False;
3325        }
3326}
3327
3328/*
3329 * Routine to remove a directory
3330 */
3331
3332static int
3333smbc_rmdir_ctx(SMBCCTX *context,
3334               const char *fname)
3335{
3336        SMBCSRV *srv;
3337        fstring server;
3338        fstring share;
3339        fstring user;
3340        fstring password;
3341        fstring workgroup;
3342        pstring path;
3343        pstring targetpath;
3344        struct cli_state *targetcli;
3345
3346        if (!context || !context->internal || 
3347            !context->internal->_initialized) {
3348
3349                errno = EINVAL;
3350                return -1;
3351
3352        }
3353
3354        if (!fname) {
3355
3356                errno = EINVAL;
3357                return -1;
3358
3359        }
3360 
3361        DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3362
3363        if (smbc_parse_path(context, fname,
3364                            workgroup, sizeof(workgroup),
3365                            server, sizeof(server),
3366                            share, sizeof(share),
3367                            path, sizeof(path),
3368                            user, sizeof(user),
3369                            password, sizeof(password),
3370                            NULL, 0))
3371        {
3372                errno = EINVAL;
3373                return -1;
3374        }
3375
3376        if (user[0] == (char)0) fstrcpy(user, context->user);
3377
3378        srv = smbc_server(context, True,
3379                          server, share, workgroup, user, password);
3380
3381        if (!srv) {
3382
3383                return -1;  /* errno set by smbc_server */
3384
3385        }
3386
3387        /*d_printf(">>>rmdir: resolving %s\n", path);*/
3388        if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3389        {
3390                d_printf("Could not resolve %s\n", path);
3391                return -1;
3392        }
3393        /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3394
3395
3396        if (!cli_rmdir(targetcli, targetpath)) {
3397
3398                errno = smbc_errno(context, targetcli);
3399
3400                if (errno == EACCES) {  /* Check if the dir empty or not */
3401
3402                        /* Local storage to avoid buffer overflows */
3403                        pstring lpath; 
3404
3405                        smbc_rmdir_dirempty = True;  /* Make this so ... */
3406
3407                        pstrcpy(lpath, targetpath);
3408                        pstrcat(lpath, "\\*");
3409
3410                        if (cli_list(targetcli, lpath,
3411                                     aDIR | aSYSTEM | aHIDDEN,
3412                                     rmdir_list_fn, NULL) < 0) {
3413
3414                                /* Fix errno to ignore latest error ... */
3415                                DEBUG(5, ("smbc_rmdir: "
3416                                          "cli_list returned an error: %d\n", 
3417                                          smbc_errno(context, targetcli)));
3418                                errno = EACCES;
3419
3420                        }
3421
3422                        if (smbc_rmdir_dirempty)
3423                                errno = EACCES;
3424                        else
3425                                errno = ENOTEMPTY;
3426
3427                }
3428
3429                return -1;
3430
3431        } 
3432
3433        return 0;
3434
3435}
3436
3437/*
3438 * Routine to return the current directory position
3439 */
3440
3441static off_t
3442smbc_telldir_ctx(SMBCCTX *context,
3443                 SMBCFILE *dir)
3444{
3445        off_t ret_val; /* Squash warnings about cast */
3446
3447        if (!context || !context->internal ||
3448            !context->internal->_initialized) {
3449
3450                errno = EINVAL;
3451                return -1;
3452
3453        }
3454
3455        if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3456
3457                errno = EBADF;
3458                return -1;
3459
3460        }
3461
3462        if (dir->file != False) { /* FIXME, should be dir, perhaps */
3463
3464                errno = ENOTDIR;
3465                return -1;
3466
3467        }
3468
3469        /*
3470         * We return the pointer here as the offset
3471         */
3472        ret_val = (off_t)(long)dir->dir_next;
3473        return ret_val;
3474
3475}
3476
3477/*
3478 * A routine to run down the list and see if the entry is OK
3479 */
3480
3481struct smbc_dir_list *
3482smbc_check_dir_ent(struct smbc_dir_list *list, 
3483                   struct smbc_dirent *dirent)
3484{
3485
3486        /* Run down the list looking for what we want */
3487
3488        if (dirent) {
3489
3490                struct smbc_dir_list *tmp = list;
3491
3492                while (tmp) {
3493
3494                        if (tmp->dirent == dirent)
3495                                return tmp;
3496
3497                        tmp = tmp->next;
3498
3499                }
3500
3501        }
3502
3503        return NULL;  /* Not found, or an error */
3504
3505}
3506
3507
3508/*
3509 * Routine to seek on a directory
3510 */
3511
3512static int
3513smbc_lseekdir_ctx(SMBCCTX *context,
3514                  SMBCFILE *dir,
3515                  off_t offset)
3516{
3517        long int l_offset = offset;  /* Handle problems of size */
3518        struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3519        struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3520
3521        if (!context || !context->internal ||
3522            !context->internal->_initialized) {
3523
3524                errno = EINVAL;
3525                return -1;
3526
3527        }
3528
3529        if (dir->file != False) { /* FIXME, should be dir, perhaps */
3530
3531                errno = ENOTDIR;
3532                return -1;
3533
3534        }
3535
3536        /* Now, check what we were passed and see if it is OK ... */
3537
3538        if (dirent == NULL) {  /* Seek to the begining of the list */
3539
3540                dir->dir_next = dir->dir_list;
3541                return 0;
3542
3543        }
3544
3545        /* Now, run down the list and make sure that the entry is OK       */
3546        /* This may need to be changed if we change the format of the list */
3547
3548        if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3549
3550                errno = EINVAL;   /* Bad entry */
3551                return -1;
3552
3553        }
3554
3555        dir->dir_next = list_ent;
3556
3557        return 0; 
3558
3559}
3560
3561/*
3562 * Routine to fstat a dir
3563 */
3564
3565static int
3566smbc_fstatdir_ctx(SMBCCTX *context,
3567                  SMBCFILE *dir,
3568                  struct stat *st)
3569{
3570
3571        if (!context || !context->internal || 
3572            !context->internal->_initialized) {
3573
3574                errno = EINVAL;
3575                return -1;
3576
3577        }
3578
3579        /* No code yet ... */
3580
3581        return 0;
3582
3583}
3584
3585static int
3586smbc_chmod_ctx(SMBCCTX *context,
3587               const char *fname,
3588               mode_t newmode)
3589{
3590        SMBCSRV *srv;
3591        fstring server;
3592        fstring share;
3593        fstring user;
3594        fstring password;
3595        fstring workgroup;
3596        pstring path;
3597        uint16 mode;
3598
3599        if (!context || !context->internal ||
3600            !context->internal->_initialized) {
3601
3602                errno = EINVAL;  /* Best I can think of ... */
3603                return -1;
3604   
3605        }
3606
3607        if (!fname) {
3608
3609                errno = EINVAL;
3610                return -1;
3611
3612        }
3613 
3614        DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
3615
3616        if (smbc_parse_path(context, fname,
3617                            workgroup, sizeof(workgroup),
3618                            server, sizeof(server),
3619                            share, sizeof(share),
3620                            path, sizeof(path),
3621                            user, sizeof(user),
3622                            password, sizeof(password),
3623                            NULL, 0)) {
3624                errno = EINVAL;
3625                return -1;
3626        }
3627
3628        if (user[0] == (char)0) fstrcpy(user, context->user);
3629
3630        srv = smbc_server(context, True,
3631                          server, share, workgroup, user, password);
3632
3633        if (!srv) {
3634                return -1;  /* errno set by smbc_server */
3635        }
3636
3637        mode = 0;
3638
3639        if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
3640        if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
3641        if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
3642        if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
3643
3644        if (!cli_setatr(srv->cli, path, mode, 0)) {
3645                errno = smbc_errno(context, srv->cli);
3646                return -1;
3647        }
3648       
3649        return 0;
3650}
3651
3652static int
3653smbc_utimes_ctx(SMBCCTX *context,
3654                const char *fname,
3655                struct timeval *tbuf)
3656{
3657        SMBCSRV *srv;
3658        fstring server;
3659        fstring share;
3660        fstring user;
3661        fstring password;
3662        fstring workgroup;
3663        pstring path;
3664        time_t access_time;
3665        time_t write_time;
3666
3667        if (!context || !context->internal ||
3668            !context->internal->_initialized) {
3669
3670                errno = EINVAL;  /* Best I can think of ... */
3671                return -1;
3672   
3673        }
3674
3675        if (!fname) {
3676
3677                errno = EINVAL;
3678                return -1;
3679
3680        }
3681 
3682        if (tbuf == NULL) {
3683                access_time = write_time = time(NULL);
3684        } else {
3685                access_time = tbuf[0].tv_sec;
3686                write_time = tbuf[1].tv_sec;
3687        }
3688
3689        if (DEBUGLVL(4)) 
3690        {
3691                char *p;
3692                char atimebuf[32];
3693                char mtimebuf[32];
3694
3695                strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
3696                atimebuf[sizeof(atimebuf) - 1] = '\0';
3697                if ((p = strchr(atimebuf, '\n')) != NULL) {
3698                        *p = '\0';
3699                }
3700
3701                strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
3702                mtimebuf[sizeof(mtimebuf) - 1] = '\0';
3703                if ((p = strchr(mtimebuf, '\n')) != NULL) {
3704                        *p = '\0';
3705                }
3706
3707                dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
3708                        fname, atimebuf, mtimebuf);
3709        }
3710
3711        if (smbc_parse_path(context, fname,
3712                            workgroup, sizeof(workgroup),
3713                            server, sizeof(server),
3714                            share, sizeof(share),
3715                            path, sizeof(path),
3716                            user, sizeof(user),
3717                            password, sizeof(password),
3718                            NULL, 0)) {
3719                errno = EINVAL;
3720                return -1;
3721        }
3722
3723        if (user[0] == (char)0) fstrcpy(user, context->user);
3724
3725        srv = smbc_server(context, True,
3726                          server, share, workgroup, user, password);
3727
3728        if (!srv) {
3729                return -1;      /* errno set by smbc_server */
3730        }
3731
3732        if (!smbc_setatr(context, srv, path,
3733                         0, access_time, write_time, 0, 0)) {
3734                return -1;      /* errno set by smbc_setatr */
3735        }
3736
3737        return 0;
3738}
3739
3740
3741/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
3742   However NT4 gives a "The information may have been modified by a
3743   computer running Windows NT 5.0" if denied ACEs do not appear before
3744   allowed ACEs. */
3745
3746static int
3747ace_compare(SEC_ACE *ace1,
3748            SEC_ACE *ace2)
3749{
3750        if (sec_ace_equal(ace1, ace2)) 
3751                return 0;
3752
3753        if (ace1->type != ace2->type) 
3754                return ace2->type - ace1->type;
3755
3756        if (sid_compare(&ace1->trustee, &ace2->trustee)) 
3757                return sid_compare(&ace1->trustee, &ace2->trustee);
3758
3759        if (ace1->flags != ace2->flags) 
3760                return ace1->flags - ace2->flags;
3761
3762        if (ace1->access_mask != ace2->access_mask) 
3763                return ace1->access_mask - ace2->access_mask;
3764
3765        if (ace1->size != ace2->size) 
3766                return ace1->size - ace2->size;
3767
3768        return memcmp(ace1, ace2, sizeof(SEC_ACE));
3769}
3770
3771
3772static void
3773sort_acl(SEC_ACL *the_acl)
3774{
3775        uint32 i;
3776        if (!the_acl) return;
3777
3778        qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
3779              QSORT_CAST ace_compare);
3780
3781        for (i=1;i<the_acl->num_aces;) {
3782                if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
3783                        int j;
3784                        for (j=i; j<the_acl->num_aces-1; j++) {
3785                                the_acl->aces[j] = the_acl->aces[j+1];
3786                        }
3787                        the_acl->num_aces--;
3788                } else {
3789                        i++;
3790                }
3791        }
3792}
3793
3794/* convert a SID to a string, either numeric or username/group */
3795static void
3796convert_sid_to_string(struct cli_state *ipc_cli,
3797                      POLICY_HND *pol,
3798                      fstring str,
3799                      BOOL numeric,
3800                      DOM_SID *sid)
3801{
3802        char **domains = NULL;
3803        char **names = NULL;
3804        enum lsa_SidType *types = NULL;
3805        struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3806        sid_to_string(str, sid);
3807
3808        if (numeric) {
3809                return;     /* no lookup desired */
3810        }
3811       
3812        if (!pipe_hnd) {
3813                return;
3814        }
3815 
3816        /* Ask LSA to convert the sid to a name */
3817
3818        if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx, 
3819                                                 pol, 1, sid, &domains, 
3820                                                 &names, &types)) ||
3821            !domains || !domains[0] || !names || !names[0]) {
3822                return;
3823        }
3824
3825        /* Converted OK */
3826
3827        slprintf(str, sizeof(fstring) - 1, "%s%s%s",
3828                 domains[0], lp_winbind_separator(),
3829                 names[0]);
3830}
3831
3832/* convert a string to a SID, either numeric or username/group */
3833static BOOL
3834convert_string_to_sid(struct cli_state *ipc_cli,
3835                      POLICY_HND *pol,
3836                      BOOL numeric,
3837                      DOM_SID *sid,
3838                      const char *str)
3839{
3840        enum lsa_SidType *types = NULL;
3841        DOM_SID *sids = NULL;
3842        BOOL result = True;
3843        struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3844
3845        if (!pipe_hnd) {
3846                return False;
3847        }
3848
3849        if (numeric) {
3850                if (strncmp(str, "S-", 2) == 0) {
3851                        return string_to_sid(sid, str);
3852                }
3853
3854                result = False;
3855                goto done;
3856        }
3857
3858        if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx, 
3859                                                  pol, 1, &str, NULL, &sids, 
3860                                                  &types))) {
3861                result = False;
3862                goto done;
3863        }
3864
3865        sid_copy(sid, &sids[0]);
3866 done:
3867
3868        return result;
3869}
3870
3871
3872/* parse an ACE in the same format as print_ace() */
3873static BOOL
3874parse_ace(struct cli_state *ipc_cli,
3875          POLICY_HND *pol,
3876          SEC_ACE *ace,
3877          BOOL numeric,
3878          char *str)
3879{
3880        char *p;
3881        const char *cp;
3882        fstring tok;
3883        unsigned int atype;
3884        unsigned int aflags;
3885        unsigned int amask;
3886        DOM_SID sid;
3887        SEC_ACCESS mask;
3888        const struct perm_value *v;
3889        struct perm_value {
3890                const char *perm;
3891                uint32 mask;
3892        };
3893
3894        /* These values discovered by inspection */
3895        static const struct perm_value special_values[] = {
3896                { "R", 0x00120089 },
3897                { "W", 0x00120116 },
3898                { "X", 0x001200a0 },
3899                { "D", 0x00010000 },
3900                { "P", 0x00040000 },
3901                { "O", 0x00080000 },
3902                { NULL, 0 },
3903        };
3904
3905        static const struct perm_value standard_values[] = {
3906                { "READ",   0x001200a9 },
3907                { "CHANGE", 0x001301bf },
3908                { "FULL",   0x001f01ff },
3909                { NULL, 0 },
3910        };
3911
3912
3913        ZERO_STRUCTP(ace);
3914        p = strchr_m(str,':');
3915        if (!p) return False;
3916        *p = '\0';
3917        p++;
3918        /* Try to parse numeric form */
3919
3920        if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3921            convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3922                goto done;
3923        }
3924
3925        /* Try to parse text form */
3926
3927        if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3928                return False;
3929        }
3930
3931        cp = p;
3932        if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3933                return False;
3934        }
3935
3936        if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3937                atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3938        } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3939                atype = SEC_ACE_TYPE_ACCESS_DENIED;
3940        } else {
3941                return False;
3942        }
3943
3944        /* Only numeric form accepted for flags at present */
3945
3946        if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
3947              sscanf(tok, "%i", &aflags))) {
3948                return False;
3949        }
3950
3951        if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3952                return False;
3953        }
3954
3955        if (strncmp(tok, "0x", 2) == 0) {
3956                if (sscanf(tok, "%i", &amask) != 1) {
3957                        return False;
3958                }
3959                goto done;
3960        }
3961
3962        for (v = standard_values; v->perm; v++) {
3963                if (strcmp(tok, v->perm) == 0) {
3964                        amask = v->mask;
3965                        goto done;
3966                }
3967        }
3968
3969        p = tok;
3970
3971        while(*p) {
3972                BOOL found = False;
3973
3974                for (v = special_values; v->perm; v++) {
3975                        if (v->perm[0] == *p) {
3976                                amask |= v->mask;
3977                                found = True;
3978                        }
3979                }
3980
3981                if (!found) return False;
3982                p++;
3983        }
3984
3985        if (*p) {
3986                return False;
3987        }
3988
3989 done:
3990        mask = amask;
3991        init_sec_ace(ace, &sid, atype, mask, aflags);
3992        return True;
3993}
3994
3995/* add an ACE to a list of ACEs in a SEC_ACL */
3996static BOOL
3997add_ace(SEC_ACL **the_acl,
3998        SEC_ACE *ace,
3999        TALLOC_CTX *ctx)
4000{
4001        SEC_ACL *newacl;
4002        SEC_ACE *aces;
4003
4004        if (! *the_acl) {
4005                (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
4006                return True;
4007        }
4008
4009        if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
4010                return False;
4011        }
4012        memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
4013        memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
4014        newacl = make_sec_acl(ctx, (*the_acl)->revision,
4015                              1+(*the_acl)->num_aces, aces);
4016        SAFE_FREE(aces);
4017        (*the_acl) = newacl;
4018        return True;
4019}
4020
4021
4022/* parse a ascii version of a security descriptor */
4023static SEC_DESC *
4024sec_desc_parse(TALLOC_CTX *ctx,
4025               struct cli_state *ipc_cli,
4026               POLICY_HND *pol,
4027               BOOL numeric,
4028               char *str)
4029{
4030        const char *p = str;
4031        fstring tok;
4032        SEC_DESC *ret = NULL;
4033        size_t sd_size;
4034        DOM_SID *group_sid=NULL;
4035        DOM_SID *owner_sid=NULL;
4036        SEC_ACL *dacl=NULL;
4037        int revision=1;
4038
4039        while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4040
4041                if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
4042                        revision = strtol(tok+9, NULL, 16);
4043                        continue;
4044                }
4045
4046                if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
4047                        if (owner_sid) {
4048                                DEBUG(5, ("OWNER specified more than once!\n"));
4049                                goto done;
4050                        }
4051                        owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4052                        if (!owner_sid ||
4053                            !convert_string_to_sid(ipc_cli, pol,
4054                                                   numeric,
4055                                                   owner_sid, tok+6)) {
4056                                DEBUG(5, ("Failed to parse owner sid\n"));
4057                                goto done;
4058                        }
4059                        continue;
4060                }
4061
4062                if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
4063                        if (owner_sid) {
4064                                DEBUG(5, ("OWNER specified more than once!\n"));
4065                                goto done;
4066                        }
4067                        owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4068                        if (!owner_sid ||
4069                            !convert_string_to_sid(ipc_cli, pol,
4070                                                   False,
4071                                                   owner_sid, tok+7)) {
4072                                DEBUG(5, ("Failed to parse owner sid\n"));
4073                                goto done;
4074                        }
4075                        continue;
4076                }
4077
4078                if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
4079                        if (group_sid) {
4080                                DEBUG(5, ("GROUP specified more than once!\n"));
4081                                goto done;
4082                        }
4083                        group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4084                        if (!group_sid ||
4085                            !convert_string_to_sid(ipc_cli, pol,
4086                                                   numeric,
4087                                                   group_sid, tok+6)) {
4088                                DEBUG(5, ("Failed to parse group sid\n"));
4089                                goto done;
4090                        }
4091                        continue;
4092                }
4093
4094                if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
4095                        if (group_sid) {
4096                                DEBUG(5, ("GROUP specified more than once!\n"));
4097                                goto done;
4098                        }
4099                        group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4100                        if (!group_sid ||
4101                            !convert_string_to_sid(ipc_cli, pol,
4102                                                   False,
4103                                                   group_sid, tok+6)) {
4104                                DEBUG(5, ("Failed to parse group sid\n"));
4105                                goto done;
4106                        }
4107                        continue;
4108                }
4109
4110                if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
4111                        SEC_ACE ace;
4112                        if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
4113                                DEBUG(5, ("Failed to parse ACL %s\n", tok));
4114                                goto done;
4115                        }
4116                        if(!add_ace(&dacl, &ace, ctx)) {
4117                                DEBUG(5, ("Failed to add ACL %s\n", tok));
4118                                goto done;
4119                        }
4120                        continue;
4121                }
4122
4123                if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
4124                        SEC_ACE ace;
4125                        if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
4126                                DEBUG(5, ("Failed to parse ACL %s\n", tok));
4127                                goto done;
4128                        }
4129                        if(!add_ace(&dacl, &ace, ctx)) {
4130                                DEBUG(5, ("Failed to add ACL %s\n", tok));
4131                                goto done;
4132                        }
4133                        continue;
4134                }
4135
4136                DEBUG(5, ("Failed to parse security descriptor\n"));
4137                goto done;
4138        }
4139
4140        ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE, 
4141                            owner_sid, group_sid, NULL, dacl, &sd_size);
4142
4143  done:
4144        SAFE_FREE(group_sid);
4145        SAFE_FREE(owner_sid);
4146
4147        return ret;
4148}
4149
4150
4151/* Obtain the current dos attributes */
4152static DOS_ATTR_DESC *
4153dos_attr_query(SMBCCTX *context,
4154               TALLOC_CTX *ctx,
4155               const char *filename,
4156               SMBCSRV *srv)
4157{
4158        struct timespec create_time_ts;
4159        struct timespec write_time_ts;
4160        struct timespec access_time_ts;
4161        struct timespec change_time_ts;
4162        SMB_OFF_T size = 0;
4163        uint16 mode = 0;
4164        SMB_INO_T inode = 0;
4165        DOS_ATTR_DESC *ret;
4166   
4167        ret = TALLOC_P(ctx, DOS_ATTR_DESC);
4168        if (!ret) {
4169                errno = ENOMEM;
4170                return NULL;
4171        }
4172
4173        /* Obtain the DOS attributes */
4174        if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
4175                         &mode, &size, 
4176                         &create_time_ts,
4177                         &access_time_ts,
4178                         &write_time_ts,
4179                         &change_time_ts, 
4180                         &inode)) {
4181       
4182                errno = smbc_errno(context, srv->cli);
4183                DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
4184                return NULL;
4185       
4186        }
4187               
4188        ret->mode = mode;
4189        ret->size = size;
4190        ret->create_time = convert_timespec_to_time_t(create_time_ts);
4191        ret->access_time = convert_timespec_to_time_t(access_time_ts);
4192        ret->write_time = convert_timespec_to_time_t(write_time_ts);
4193        ret->change_time = convert_timespec_to_time_t(change_time_ts);
4194        ret->inode = inode;
4195
4196        return ret;
4197}
4198
4199
4200/* parse a ascii version of a security descriptor */
4201static void
4202dos_attr_parse(SMBCCTX *context,
4203               DOS_ATTR_DESC *dad,
4204               SMBCSRV *srv,
4205               char *str)
4206{
4207        int n;
4208        const char *p = str;
4209        fstring tok;
4210        struct {
4211                const char * create_time_attr;
4212                const char * access_time_attr;
4213                const char * write_time_attr;
4214                const char * change_time_attr;
4215        } attr_strings;
4216
4217        /* Determine whether to use old-style or new-style attribute names */
4218        if (context->internal->_full_time_names) {
4219                /* new-style names */
4220                attr_strings.create_time_attr = "CREATE_TIME";
4221                attr_strings.access_time_attr = "ACCESS_TIME";
4222                attr_strings.write_time_attr = "WRITE_TIME";
4223                attr_strings.change_time_attr = "CHANGE_TIME";
4224        } else {
4225                /* old-style names */
4226                attr_strings.create_time_attr = NULL;
4227                attr_strings.access_time_attr = "A_TIME";
4228                attr_strings.write_time_attr = "M_TIME";
4229                attr_strings.change_time_attr = "C_TIME";
4230        }
4231
4232        /* if this is to set the entire ACL... */
4233        if (*str == '*') {
4234                /* ... then increment past the first colon if there is one */
4235                if ((p = strchr(str, ':')) != NULL) {
4236                        ++p;
4237                } else {
4238                        p = str;
4239                }
4240        }
4241
4242        while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4243
4244                if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
4245                        dad->mode = strtol(tok+5, NULL, 16);
4246                        continue;
4247                }
4248
4249                if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
4250                        dad->size = (SMB_OFF_T)atof(tok+5);
4251                        continue;
4252                }
4253
4254                n = strlen(attr_strings.access_time_attr);
4255                if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
4256                        dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
4257                        continue;
4258                }
4259
4260                n = strlen(attr_strings.change_time_attr);
4261                if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
4262                        dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
4263                        continue;
4264                }
4265
4266                n = strlen(attr_strings.write_time_attr);
4267                if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
4268                        dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
4269                        continue;
4270                }
4271
4272                if (attr_strings.create_time_attr != NULL) {
4273                        n = strlen(attr_strings.create_time_attr);
4274                        if (StrnCaseCmp(tok, attr_strings.create_time_attr,
4275                                        n) == 0) {
4276                                dad->create_time = (time_t)strtol(tok+n+1,
4277                                                                  NULL, 10);
4278                                continue;
4279                        }
4280                }
4281
4282                if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
4283                        dad->inode = (SMB_INO_T)atof(tok+6);
4284                        continue;
4285                }
4286        }
4287}
4288
4289/*****************************************************
4290 Retrieve the acls for a file.
4291*******************************************************/
4292
4293static int
4294cacl_get(SMBCCTX *context,
4295         TALLOC_CTX *ctx,
4296         SMBCSRV *srv,
4297         struct cli_state *ipc_cli,
4298         POLICY_HND *pol,
4299         char *filename,
4300         char *attr_name,
4301         char *buf,
4302         int bufsize)
4303{
4304        uint32 i;
4305        int n = 0;
4306        int n_used;
4307        BOOL all;
4308        BOOL all_nt;
4309        BOOL all_nt_acls;
4310        BOOL all_dos;
4311        BOOL some_nt;
4312        BOOL some_dos;
4313        BOOL exclude_nt_revision = False;
4314        BOOL exclude_nt_owner = False;
4315        BOOL exclude_nt_group = False;
4316        BOOL exclude_nt_acl = False;
4317        BOOL exclude_dos_mode = False;
4318        BOOL exclude_dos_size = False;
4319        BOOL exclude_dos_create_time = False;
4320        BOOL exclude_dos_access_time = False;
4321        BOOL exclude_dos_write_time = False;
4322        BOOL exclude_dos_change_time = False;
4323        BOOL exclude_dos_inode = False;
4324        BOOL numeric = True;
4325        BOOL determine_size = (bufsize == 0);
4326        int fnum = -1;
4327        SEC_DESC *sd;
4328        fstring sidstr;
4329        fstring name_sandbox;
4330        char *name;
4331        char *pExclude;
4332        char *p;
4333        struct timespec create_time_ts;
4334        struct timespec write_time_ts;
4335        struct timespec access_time_ts;
4336        struct timespec change_time_ts;
4337        time_t create_time = (time_t)0;
4338        time_t write_time = (time_t)0;
4339        time_t access_time = (time_t)0;
4340        time_t change_time = (time_t)0;
4341        SMB_OFF_T size = 0;
4342        uint16 mode = 0;
4343        SMB_INO_T ino = 0;
4344        struct cli_state *cli = srv->cli;
4345        struct {
4346                const char * create_time_attr;
4347                const char * access_time_attr;
4348                const char * write_time_attr;
4349                const char * change_time_attr;
4350        } attr_strings;
4351        struct {
4352                const char * create_time_attr;
4353                const char * access_time_attr;
4354                const char * write_time_attr;
4355                const char * change_time_attr;
4356        } excl_attr_strings;
4357
4358        /* Determine whether to use old-style or new-style attribute names */
4359        if (context->internal->_full_time_names) {
4360                /* new-style names */
4361                attr_strings.create_time_attr = "CREATE_TIME";
4362                attr_strings.access_time_attr = "ACCESS_TIME";
4363                attr_strings.write_time_attr = "WRITE_TIME";
4364                attr_strings.change_time_attr = "CHANGE_TIME";
4365
4366                excl_attr_strings.create_time_attr = "CREATE_TIME";
4367                excl_attr_strings.access_time_attr = "ACCESS_TIME";
4368                excl_attr_strings.write_time_attr = "WRITE_TIME";
4369                excl_attr_strings.change_time_attr = "CHANGE_TIME";
4370        } else {
4371                /* old-style names */
4372                attr_strings.create_time_attr = NULL;
4373                attr_strings.access_time_attr = "A_TIME";
4374                attr_strings.write_time_attr = "M_TIME";
4375                attr_strings.change_time_attr = "C_TIME";
4376
4377                excl_attr_strings.create_time_attr = NULL;
4378                excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
4379                excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
4380                excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
4381        }
4382
4383        /* Copy name so we can strip off exclusions (if any are specified) */
4384        strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
4385
4386        /* Ensure name is null terminated */
4387        name_sandbox[sizeof(name_sandbox) - 1] = '\0';
4388
4389        /* Play in the sandbox */
4390        name = name_sandbox;
4391
4392        /* If there are any exclusions, point to them and mask them from name */
4393        if ((pExclude = strchr(name, '!')) != NULL)
4394        {
4395                *pExclude++ = '\0';
4396        }
4397
4398        all = (StrnCaseCmp(name, "system.*", 8) == 0);
4399        all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
4400        all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
4401        all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
4402        some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
4403        some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
4404        numeric = (* (name + strlen(name) - 1) != '+');
4405
4406        /* Look for exclusions from "all" requests */
4407        if (all || all_nt || all_dos) {
4408
4409                /* Exclusions are delimited by '!' */
4410                for (;
4411                     pExclude != NULL;
4412                     pExclude = (p == NULL ? NULL : p + 1)) {
4413
4414                /* Find end of this exclusion name */
4415                if ((p = strchr(pExclude, '!')) != NULL)
4416                {
4417                    *p = '\0';
4418                }
4419
4420                /* Which exclusion name is this? */
4421                if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
4422                    exclude_nt_revision = True;
4423                }
4424                else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
4425                    exclude_nt_owner = True;
4426                }
4427                else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
4428                    exclude_nt_group = True;
4429                }
4430                else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
4431                    exclude_nt_acl = True;
4432                }
4433                else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
4434                    exclude_dos_mode = True;
4435                }
4436                else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
4437                    exclude_dos_size = True;
4438                }
4439                else if (excl_attr_strings.create_time_attr != NULL &&
4440                         StrCaseCmp(pExclude,
4441                                    excl_attr_strings.change_time_attr) == 0) {
4442                    exclude_dos_create_time = True;
4443                }
4444                else if (StrCaseCmp(pExclude,
4445                                    excl_attr_strings.access_time_attr) == 0) {
4446                    exclude_dos_access_time = True;
4447                }
4448                else if (StrCaseCmp(pExclude,
4449                                    excl_attr_strings.write_time_attr) == 0) {
4450                    exclude_dos_write_time = True;
4451                }
4452                else if (StrCaseCmp(pExclude,
4453                                    excl_attr_strings.change_time_attr) == 0) {
4454                    exclude_dos_change_time = True;
4455                }
4456                else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4457                    exclude_dos_inode = True;
4458                }
4459                else {
4460                    DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4461                              pExclude));
4462                    errno = ENOATTR;
4463                    return -1;
4464                }
4465            }
4466        }
4467
4468        n_used = 0;
4469
4470        /*
4471         * If we are (possibly) talking to an NT or new system and some NT
4472         * attributes have been requested...
4473         */
4474        if (ipc_cli && (all || some_nt || all_nt_acls)) {
4475                /* Point to the portion after "system.nt_sec_desc." */
4476                name += 19;     /* if (all) this will be invalid but unused */
4477
4478                /* ... then obtain any NT attributes which were requested */
4479                fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4480
4481                if (fnum == -1) {
4482                        DEBUG(5, ("cacl_get failed to open %s: %s\n",
4483                                  filename, cli_errstr(cli)));
4484                        errno = 0;
4485                        return -1;
4486                }
4487
4488                sd = cli_query_secdesc(cli, fnum, ctx);
4489
4490                if (!sd) {
4491                        DEBUG(5,
4492                              ("cacl_get Failed to query old descriptor\n"));
4493                        errno = 0;
4494                        return -1;
4495                }
4496
4497                cli_close(cli, fnum);
4498
4499                if (! exclude_nt_revision) {
4500                        if (all || all_nt) {
4501                                if (determine_size) {
4502                                        p = talloc_asprintf(ctx,
4503                                                            "REVISION:%d",
4504                                                            sd->revision);
4505                                        if (!p) {
4506                                                errno = ENOMEM;
4507                                                return -1;
4508                                        }
4509                                        n = strlen(p);
4510                                } else {
4511                                        n = snprintf(buf, bufsize,
4512                                                     "REVISION:%d",
4513                                                     sd->revision);
4514                                }
4515                        } else if (StrCaseCmp(name, "revision") == 0) {
4516                                if (determine_size) {
4517                                        p = talloc_asprintf(ctx, "%d",
4518                                                            sd->revision);
4519                                        if (!p) {
4520                                                errno = ENOMEM;
4521                                                return -1;
4522                                        }
4523                                        n = strlen(p);
4524                                } else {
4525                                        n = snprintf(buf, bufsize, "%d",
4526                                                     sd->revision);
4527                                }
4528                        }
4529       
4530                        if (!determine_size && n > bufsize) {
4531                                errno = ERANGE;
4532                                return -1;
4533                        }
4534                        buf += n;
4535                        n_used += n;
4536                        bufsize -= n;
4537                }
4538
4539                if (! exclude_nt_owner) {
4540                        /* Get owner and group sid */
4541                        if (sd->owner_sid) {
4542                                convert_sid_to_string(ipc_cli, pol,
4543                                                      sidstr,
4544                                                      numeric,
4545                                                      sd->owner_sid);
4546                        } else {
4547                                fstrcpy(sidstr, "");
4548                        }
4549
4550                        if (all || all_nt) {
4551                                if (determine_size) {
4552                                        p = talloc_asprintf(ctx, ",OWNER:%s",
4553                                                            sidstr);
4554                                        if (!p) {
4555                                                errno = ENOMEM;
4556                                                return -1;
4557                                        }
4558                                        n = strlen(p);
4559                                } else {
4560                                        n = snprintf(buf, bufsize,
4561                                                     ",OWNER:%s", sidstr);
4562                                }
4563                        } else if (StrnCaseCmp(name, "owner", 5) == 0) {
4564                                if (determine_size) {
4565                                        p = talloc_asprintf(ctx, "%s", sidstr);
4566                                        if (!p) {
4567                                                errno = ENOMEM;
4568                                                return -1;
4569                                        }
4570                                        n = strlen(p);
4571                                } else {
4572                                        n = snprintf(buf, bufsize, "%s",
4573                                                     sidstr);
4574                                }
4575                        }
4576
4577                        if (!determine_size && n > bufsize) {
4578                                errno = ERANGE;
4579                                return -1;
4580                        }
4581                        buf += n;
4582                        n_used += n;
4583                        bufsize -= n;
4584                }
4585
4586                if (! exclude_nt_group) {
4587                        if (sd->group_sid) {
4588                                convert_sid_to_string(ipc_cli, pol,
4589                                                      sidstr, numeric,
4590                                                      sd->group_sid);
4591                        } else {
4592                                fstrcpy(sidstr, "");
4593                        }
4594
4595                        if (all || all_nt) {
4596                                if (determine_size) {
4597                                        p = talloc_asprintf(ctx, ",GROUP:%s",
4598                                                            sidstr);
4599                                        if (!p) {
4600                                                errno = ENOMEM;
4601                                                return -1;
4602                                        }
4603                                        n = strlen(p);
4604                                } else {
4605                                        n = snprintf(buf, bufsize,
4606                                                     ",GROUP:%s", sidstr);
4607                                }
4608                        } else if (StrnCaseCmp(name, "group", 5) == 0) {
4609                                if (determine_size) {
4610                                        p = talloc_asprintf(ctx, "%s", sidstr);
4611                                        if (!p) {
4612                                                errno = ENOMEM;
4613                                                return -1;
4614                                        }
4615                                        n = strlen(p);
4616                                } else {
4617                                        n = snprintf(buf, bufsize,
4618                                                     "%s", sidstr);
4619                                }
4620                        }
4621
4622                        if (!determine_size && n > bufsize) {
4623                                errno = ERANGE;
4624                                return -1;
4625                        }
4626                        buf += n;
4627                        n_used += n;
4628                        bufsize -= n;
4629                }
4630
4631                if (! exclude_nt_acl) {
4632                        /* Add aces to value buffer  */
4633                        for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
4634
4635                                SEC_ACE *ace = &sd->dacl->aces[i];
4636                                convert_sid_to_string(ipc_cli, pol,
4637                                                      sidstr, numeric,
4638                                                      &ace->trustee);
4639
4640                                if (all || all_nt) {
4641                                        if (determine_size) {
4642                                                p = talloc_asprintf(
4643                                                        ctx, 
4644                                                        ",ACL:"
4645                                                        "%s:%d/%d/0x%08x", 
4646                                                        sidstr,
4647                                                        ace->type,
4648                                                        ace->flags,
4649                                                        ace->access_mask);
4650                                                if (!p) {
4651                                                        errno = ENOMEM;
4652                                                        return -1;
4653                                                }
4654                                                n = strlen(p);
4655                                        } else {
4656                                                n = snprintf(
4657                                                        buf, bufsize,
4658                                                        ",ACL:%s:%d/%d/0x%08x", 
4659                                                        sidstr,
4660                                                        ace->type,
4661                                                        ace->flags,
4662                                                        ace->access_mask);
4663                                        }
4664                                } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
4665                                            StrCaseCmp(name+3, sidstr) == 0) ||
4666                                           (StrnCaseCmp(name, "acl+", 4) == 0 &&
4667                                            StrCaseCmp(name+4, sidstr) == 0)) {
4668                                        if (determine_size) {
4669                                                p = talloc_asprintf(
4670                                                        ctx, 
4671                                                        "%d/%d/0x%08x", 
4672                                                        ace->type,
4673                                                        ace->flags,
4674                                                        ace->access_mask);
4675                                                if (!p) {
4676                                                        errno = ENOMEM;
4677                                                        return -1;
4678                                                }
4679                                                n = strlen(p);
4680                                        } else {
4681                                                n = snprintf(buf, bufsize,
4682                                                             "%d/%d/0x%08x", 
4683                                                             ace->type,
4684                                                             ace->flags,
4685                                                             ace->access_mask);
4686                                        }
4687                                } else if (all_nt_acls) {
4688                                        if (determine_size) {
4689                                                p = talloc_asprintf(
4690                                                        ctx, 
4691                                                        "%s%s:%d/%d/0x%08x",
4692                                                        i ? "," : "",
4693                                                        sidstr,
4694                                                        ace->type,
4695                                                        ace->flags,
4696                                                        ace->access_mask);
4697                                                if (!p) {
4698                                                        errno = ENOMEM;
4699                                                        return -1;
4700                                                }
4701                                                n = strlen(p);
4702                                        } else {
4703                                                n = snprintf(buf, bufsize,
4704                                                             "%s%s:%d/%d/0x%08x",
4705                                                             i ? "," : "",
4706                                                             sidstr,
4707                                                             ace->type,
4708                                                             ace->flags,
4709                                                             ace->access_mask);
4710                                        }
4711                                }
4712                                if (n > bufsize) {
4713                                        errno = ERANGE;
4714                                        return -1;
4715                                }
4716                                buf += n;
4717                                n_used += n;
4718                                bufsize -= n;
4719                        }
4720                }
4721
4722                /* Restore name pointer to its original value */
4723                name -= 19;
4724        }
4725
4726        if (all || some_dos) {
4727                /* Point to the portion after "system.dos_attr." */
4728                name += 16;     /* if (all) this will be invalid but unused */
4729
4730                /* Obtain the DOS attributes */
4731                if (!smbc_getatr(context, srv, filename, &mode, &size, 
4732                                 &create_time_ts,
4733                                 &access_time_ts,
4734                                 &write_time_ts,
4735                                 &change_time_ts,
4736                                 &ino)) {
4737                       
4738                        errno = smbc_errno(context, srv->cli);
4739                        return -1;
4740                       
4741                }
4742
4743                create_time = convert_timespec_to_time_t(create_time_ts);
4744                access_time = convert_timespec_to_time_t(access_time_ts);
4745                write_time = convert_timespec_to_time_t(write_time_ts);
4746                change_time = convert_timespec_to_time_t(change_time_ts);
4747
4748                if (! exclude_dos_mode) {
4749                        if (all || all_dos) {
4750                                if (determine_size) {
4751                                        p = talloc_asprintf(ctx,
4752                                                            "%sMODE:0x%x",
4753                                                            (ipc_cli &&
4754                                                             (all || some_nt)
4755                                                             ? ","
4756                                                             : ""),
4757                                                            mode);
4758                                        if (!p) {
4759                                                errno = ENOMEM;
4760                                                return -1;
4761                                        }
4762                                        n = strlen(p);
4763                                } else {
4764                                        n = snprintf(buf, bufsize,
4765                                                     "%sMODE:0x%x",
4766                                                     (ipc_cli &&
4767                                                      (all || some_nt)
4768                                                      ? ","
4769                                                      : ""),
4770                                                     mode);
4771                                }
4772                        } else if (StrCaseCmp(name, "mode") == 0) {
4773                                if (determine_size) {
4774                                        p = talloc_asprintf(ctx, "0x%x", mode);
4775                                        if (!p) {
4776                                                errno = ENOMEM;
4777                                                return -1;
4778                                        }
4779                                        n = strlen(p);
4780                                } else {
4781                                        n = snprintf(buf, bufsize,
4782                                                     "0x%x", mode);
4783                                }
4784                        }
4785       
4786                        if (!determine_size && n > bufsize) {
4787                                errno = ERANGE;
4788                                return -1;
4789                        }
4790                        buf += n;
4791                        n_used += n;
4792                        bufsize -= n;
4793                }
4794
4795                if (! exclude_dos_size) {
4796                        if (all || all_dos) {
4797                                if (determine_size) {
4798                                        p = talloc_asprintf(
4799                                                ctx,
4800                                                ",SIZE:%.0f",
4801                                                (double)size);
4802                                        if (!p) {
4803                                                errno = ENOMEM;
4804                                                return -1;
4805                                        }
4806                                        n = strlen(p);
4807                                } else {
4808                                        n = snprintf(buf, bufsize,
4809                                                     ",SIZE:%.0f",
4810                                                     (double)size);
4811                                }
4812                        } else if (StrCaseCmp(name, "size") == 0) {
4813                                if (determine_size) {
4814                                        p = talloc_asprintf(
4815                                                ctx,
4816                                                "%.0f",
4817                                                (double)size);
4818                                        if (!p) {
4819                                                errno = ENOMEM;
4820                                                return -1;
4821                                        }
4822                                        n = strlen(p);
4823                                } else {
4824                                        n = snprintf(buf, bufsize,
4825                                                     "%.0f",
4826                                                     (double)size);
4827                                }
4828                        }
4829       
4830                        if (!determine_size && n > bufsize) {
4831                                errno = ERANGE;
4832                                return -1;
4833                        }
4834                        buf += n;
4835                        n_used += n;
4836                        bufsize -= n;
4837                }
4838
4839                if (! exclude_dos_create_time &&
4840                    attr_strings.create_time_attr != NULL) {
4841                        if (all || all_dos) {
4842                                if (determine_size) {
4843                                        p = talloc_asprintf(ctx,
4844                                                            ",%s:%lu",
4845                                                            attr_strings.create_time_attr,
4846                                                            create_time);
4847                                        if (!p) {
4848                                                errno = ENOMEM;
4849                                                return -1;
4850                                        }
4851                                        n = strlen(p);
4852                                } else {
4853                                        n = snprintf(buf, bufsize,
4854                                                     ",%s:%lu",
4855                                                     attr_strings.create_time_attr,
4856                                                     create_time);
4857                                }
4858                        } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
4859                                if (determine_size) {
4860                                        p = talloc_asprintf(ctx, "%lu", create_time);
4861                                        if (!p) {
4862                                                errno = ENOMEM;
4863                                                return -1;
4864                                        }
4865                                        n = strlen(p);
4866                                } else {
4867                                        n = snprintf(buf, bufsize,
4868                                                     "%lu", create_time);
4869                                }
4870                        }
4871       
4872                        if (!determine_size && n > bufsize) {
4873                                errno = ERANGE;
4874                                return -1;
4875                        }
4876                        buf += n;
4877                        n_used += n;
4878                        bufsize -= n;
4879                }
4880
4881                if (! exclude_dos_access_time) {
4882                        if (all || all_dos) {
4883                                if (determine_size) {
4884                                        p = talloc_asprintf(ctx,
4885                                                            ",%s:%lu",
4886                                                            attr_strings.access_time_attr,
4887                                                            access_time);
4888                                        if (!p) {
4889                                                errno = ENOMEM;
4890                                                return -1;
4891                                        }
4892                                        n = strlen(p);
4893                                } else {
4894                                        n = snprintf(buf, bufsize,
4895                                                     ",%s:%lu",
4896                                                     attr_strings.access_time_attr,
4897                                                     access_time);
4898                                }
4899                        } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
4900                                if (determine_size) {
4901                                        p = talloc_asprintf(ctx, "%lu", access_time);
4902                                        if (!p) {
4903                                                errno = ENOMEM;
4904                                                return -1;
4905                                        }
4906                                        n = strlen(p);
4907                                } else {
4908                                        n = snprintf(buf, bufsize,
4909                                                     "%lu", access_time);
4910                                }
4911                        }
4912       
4913                        if (!determine_size && n > bufsize) {
4914                                errno = ERANGE;
4915                                return -1;
4916                        }
4917                        buf += n;
4918                        n_used += n;
4919                        bufsize -= n;
4920                }
4921
4922                if (! exclude_dos_write_time) {
4923                        if (all || all_dos) {
4924                                if (determine_size) {
4925                                        p = talloc_asprintf(ctx,
4926                                                            ",%s:%lu",
4927                                                            attr_strings.write_time_attr,
4928                                                            write_time);
4929                                        if (!p) {
4930                                                errno = ENOMEM;
4931                                                return -1;
4932                                        }
4933                                        n = strlen(p);
4934                                } else {
4935                                        n = snprintf(buf, bufsize,
4936                                                     ",%s:%lu",
4937                                                     attr_strings.write_time_attr,
4938                                                     write_time);
4939                                }
4940                        } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
4941                                if (determine_size) {
4942                                        p = talloc_asprintf(ctx, "%lu", write_time);
4943                                        if (!p) {
4944                                                errno = ENOMEM;
4945                                                return -1;
4946                                        }
4947                                        n = strlen(p);
4948                                } else {
4949                                        n = snprintf(buf, bufsize,
4950                                                     "%lu", write_time);
4951                                }
4952                        }
4953       
4954                        if (!determine_size && n > bufsize) {
4955                                errno = ERANGE;
4956                                return -1;
4957                        }
4958                        buf += n;
4959                        n_used += n;
4960                        bufsize -= n;
4961                }
4962
4963                if (! exclude_dos_change_time) {
4964                        if (all || all_dos) {
4965                                if (determine_size) {
4966                                        p = talloc_asprintf(ctx,
4967                                                            ",%s:%lu",
4968                                                            attr_strings.change_time_attr,
4969                                                            change_time);
4970                                        if (!p) {
4971                                                errno = ENOMEM;
4972                                                return -1;
4973                                        }
4974                                        n = strlen(p);
4975                                } else {
4976                                        n = snprintf(buf, bufsize,
4977                                                     ",%s:%lu",
4978                                                     attr_strings.change_time_attr,
4979                                                     change_time);
4980                                }
4981                        } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
4982                                if (determine_size) {
4983                                        p = talloc_asprintf(ctx, "%lu", change_time);
4984                                        if (!p) {
4985                                                errno = ENOMEM;
4986                                                return -1;
4987                                        }
4988                                        n = strlen(p);
4989                                } else {
4990                                        n = snprintf(buf, bufsize,
4991                                                     "%lu", change_time);
4992                                }
4993                        }
4994       
4995                        if (!determine_size && n > bufsize) {
4996                                errno = ERANGE;
4997                                return -1;
4998                        }
4999                        buf += n;
5000                        n_used += n;
5001                        bufsize -= n;
5002                }
5003
5004                if (! exclude_dos_inode) {
5005                        if (all || all_dos) {
5006                                if (determine_size) {
5007                                        p = talloc_asprintf(
5008                                                ctx,
5009                                                ",INODE:%.0f",
5010                                                (double)ino);
5011                                        if (!p) {
5012                                                errno = ENOMEM;
5013                                                return -1;
5014                                        }
5015                                        n = strlen(p);
5016                                } else {
5017                                        n = snprintf(buf, bufsize,
5018                                                     ",INODE:%.0f",
5019                                                     (double) ino);
5020                                }
5021                        } else if (StrCaseCmp(name, "inode") == 0) {
5022                                if (determine_size) {
5023                                        p = talloc_asprintf(
5024                                                ctx,
5025                                                "%.0f",
5026                                                (double) ino);
5027                                        if (!p) {
5028                                                errno = ENOMEM;
5029                                                return -1;
5030                                        }
5031                                        n = strlen(p);
5032                                } else {
5033                                        n = snprintf(buf, bufsize,
5034                                                     "%.0f",
5035                                                     (double) ino);
5036                                }
5037                        }
5038       
5039                        if (!determine_size && n > bufsize) {
5040                                errno = ERANGE;
5041                                return -1;
5042                        }
5043                        buf += n;
5044                        n_used += n;
5045                        bufsize -= n;
5046                }
5047
5048                /* Restore name pointer to its original value */
5049                name -= 16;
5050        }
5051
5052        if (n_used == 0) {
5053                errno = ENOATTR;
5054                return -1;
5055        }
5056
5057        return n_used;
5058}
5059
5060
5061/*****************************************************
5062set the ACLs on a file given an ascii description
5063*******************************************************/
5064static int
5065cacl_set(TALLOC_CTX *ctx,
5066         struct cli_state *cli,
5067         struct cli_state *ipc_cli,
5068         POLICY_HND *pol,
5069         const char *filename,
5070         const char *the_acl,
5071         int mode,
5072         int flags)
5073{
5074        int fnum;
5075        int err = 0;
5076        SEC_DESC *sd = NULL, *old;
5077        SEC_ACL *dacl = NULL;
5078        DOM_SID *owner_sid = NULL; 
5079        DOM_SID *group_sid = NULL;
5080        uint32 i, j;
5081        size_t sd_size;
5082        int ret = 0;
5083        char *p;
5084        BOOL numeric = True;
5085
5086        /* the_acl will be null for REMOVE_ALL operations */
5087        if (the_acl) {
5088                numeric = ((p = strchr(the_acl, ':')) != NULL &&
5089                           p > the_acl &&
5090                           p[-1] != '+');
5091
5092                /* if this is to set the entire ACL... */
5093                if (*the_acl == '*') {
5094                        /* ... then increment past the first colon */
5095                        the_acl = p + 1;
5096                }
5097
5098                sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
5099                                    CONST_DISCARD(char *, the_acl));
5100
5101                if (!sd) {
5102                        errno = EINVAL;
5103                        return -1;
5104                }
5105        }
5106
5107        /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
5108           that doesn't deref sd */
5109
5110        if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
5111                errno = EINVAL;
5112                return -1;
5113        }
5114
5115        /* The desired access below is the only one I could find that works
5116           with NT4, W2KP and Samba */
5117
5118        fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
5119
5120        if (fnum == -1) {
5121                DEBUG(5, ("cacl_set failed to open %s: %s\n",
5122                          filename, cli_errstr(cli)));
5123                errno = 0;
5124                return -1;
5125        }
5126
5127        old = cli_query_secdesc(cli, fnum, ctx);
5128
5129        if (!old) {
5130                DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
5131                errno = 0;
5132                return -1;
5133        }
5134
5135        cli_close(cli, fnum);
5136
5137        switch (mode) {
5138        case SMBC_XATTR_MODE_REMOVE_ALL:
5139                old->dacl->num_aces = 0;
5140                SAFE_FREE(old->dacl->aces);
5141                SAFE_FREE(old->dacl);
5142                old->dacl = NULL;
5143                dacl = old->dacl;
5144                break;
5145
5146        case SMBC_XATTR_MODE_REMOVE:
5147                for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5148                        BOOL found = False;
5149
5150                        for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5151                                if (sec_ace_equal(&sd->dacl->aces[i],
5152                                                  &old->dacl->aces[j])) {
5153                                        uint32 k;
5154                                        for (k=j; k<old->dacl->num_aces-1;k++) {
5155                                                old->dacl->aces[k] =
5156                                                        old->dacl->aces[k+1];
5157                                        }
5158                                        old->dacl->num_aces--;
5159                                        if (old->dacl->num_aces == 0) {
5160                                                SAFE_FREE(old->dacl->aces);
5161                                                SAFE_FREE(old->dacl);
5162                                                old->dacl = NULL;
5163                                        }
5164                                        found = True;
5165                                        dacl = old->dacl;
5166                                        break;
5167                                }
5168                        }
5169
5170                        if (!found) {
5171                                err = ENOATTR;
5172                                ret = -1;
5173                                goto failed;
5174                        }
5175                }
5176                break;
5177
5178        case SMBC_XATTR_MODE_ADD:
5179                for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5180                        BOOL found = False;
5181
5182                        for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5183                                if (sid_equal(&sd->dacl->aces[i].trustee,
5184                                              &old->dacl->aces[j].trustee)) {
5185                                        if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
5186                                                err = EEXIST;
5187                                                ret = -1;
5188                                                goto failed;
5189                                        }
5190                                        old->dacl->aces[j] = sd->dacl->aces[i];
5191                                        ret = -1;
5192                                        found = True;
5193                                }
5194                        }
5195
5196                        if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
5197                                err = ENOATTR;
5198                                ret = -1;
5199                                goto failed;
5200                        }
5201                       
5202                        for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5203                                add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
5204                        }
5205                }
5206                dacl = old->dacl;
5207                break;
5208
5209        case SMBC_XATTR_MODE_SET:
5210                old = sd;
5211                owner_sid = old->owner_sid;
5212                group_sid = old->group_sid;
5213                dacl = old->dacl;
5214                break;
5215
5216        case SMBC_XATTR_MODE_CHOWN:
5217                owner_sid = sd->owner_sid;
5218                break;
5219
5220        case SMBC_XATTR_MODE_CHGRP:
5221                group_sid = sd->group_sid;
5222                break;
5223        }
5224
5225        /* Denied ACE entries must come before allowed ones */
5226        sort_acl(old->dacl);
5227
5228        /* Create new security descriptor and set it */
5229        sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, 
5230                           owner_sid, group_sid, NULL, dacl, &sd_size);
5231
5232        fnum = cli_nt_create(cli, filename,
5233                             WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
5234
5235        if (fnum == -1) {
5236                DEBUG(5, ("cacl_set failed to open %s: %s\n",
5237                          filename, cli_errstr(cli)));
5238                errno = 0;
5239                return -1;
5240        }
5241
5242        if (!cli_set_secdesc(cli, fnum, sd)) {
5243                DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5244                ret = -1;
5245        }
5246
5247        /* Clean up */
5248
5249 failed:
5250        cli_close(cli, fnum);
5251
5252        if (err != 0) {
5253                errno = err;
5254        }
5255       
5256        return ret;
5257}
5258
5259
5260static int
5261smbc_setxattr_ctx(SMBCCTX *context,
5262                  const char *fname,
5263                  const char *name,
5264                  const void *value,
5265                  size_t size,
5266                  int flags)
5267{
5268        int ret;
5269        int ret2;
5270        SMBCSRV *srv;
5271        SMBCSRV *ipc_srv;
5272        fstring server;
5273        fstring share;
5274        fstring user;
5275        fstring password;
5276        fstring workgroup;
5277        pstring path;
5278        TALLOC_CTX *ctx;
5279        POLICY_HND pol;
5280        DOS_ATTR_DESC *dad;
5281        struct {
5282                const char * create_time_attr;
5283                const char * access_time_attr;
5284                const char * write_time_attr;
5285                const char * change_time_attr;
5286        } attr_strings;
5287
5288        if (!context || !context->internal ||
5289            !context->internal->_initialized) {
5290
5291                errno = EINVAL;  /* Best I can think of ... */
5292                return -1;
5293   
5294        }
5295
5296        if (!fname) {
5297
5298                errno = EINVAL;
5299                return -1;
5300
5301        }
5302 
5303        DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5304                  fname, name, (int) size, (const char*)value));
5305
5306        if (smbc_parse_path(context, fname,
5307                            workgroup, sizeof(workgroup),
5308                            server, sizeof(server),
5309                            share, sizeof(share),
5310                            path, sizeof(path),
5311                            user, sizeof(user),
5312                            password, sizeof(password),
5313                            NULL, 0)) {
5314                errno = EINVAL;
5315                return -1;
5316        }
5317
5318        if (user[0] == (char)0) fstrcpy(user, context->user);
5319
5320        srv = smbc_server(context, True,
5321                          server, share, workgroup, user, password);
5322        if (!srv) {
5323                return -1;  /* errno set by smbc_server */
5324        }
5325
5326        if (! srv->no_nt_session) {
5327                ipc_srv = smbc_attr_server(context, server, share,
5328                                           workgroup, user, password,
5329                                           &pol);
5330                srv->no_nt_session = True;
5331        } else {
5332                ipc_srv = NULL;
5333        }
5334       
5335        ctx = talloc_init("smbc_setxattr");
5336        if (!ctx) {
5337                errno = ENOMEM;
5338                return -1;
5339        }
5340
5341        /*
5342         * Are they asking to set the entire set of known attributes?
5343         */
5344        if (StrCaseCmp(name, "system.*") == 0 ||
5345            StrCaseCmp(name, "system.*+") == 0) {
5346                /* Yup. */
5347                char *namevalue =
5348                        talloc_asprintf(ctx, "%s:%s",
5349                                        name+7, (const char *) value);
5350                if (! namevalue) {
5351                        errno = ENOMEM;
5352                        ret = -1;
5353                        return -1;
5354                }
5355
5356                if (ipc_srv) {
5357                        ret = cacl_set(ctx, srv->cli,
5358                                       ipc_srv->cli, &pol, path,
5359                                       namevalue,
5360                                       (*namevalue == '*'
5361                                        ? SMBC_XATTR_MODE_SET
5362                                        : SMBC_XATTR_MODE_ADD),
5363                                       flags);
5364                } else {
5365                        ret = 0;
5366                }
5367
5368                /* get a DOS Attribute Descriptor with current attributes */
5369                dad = dos_attr_query(context, ctx, path, srv);
5370                if (dad) {
5371                        /* Overwrite old with new, using what was provided */
5372                        dos_attr_parse(context, dad, srv, namevalue);
5373
5374                        /* Set the new DOS attributes */
5375                        if (! smbc_setatr(context, srv, path,
5376                                          dad->create_time,
5377                                          dad->access_time,
5378                                          dad->write_time,
5379                                          dad->change_time,
5380                                          dad->mode)) {
5381
5382                                /* cause failure if NT failed too */
5383                                dad = NULL; 
5384                        }
5385                }
5386
5387                /* we only fail if both NT and DOS sets failed */
5388                if (ret < 0 && ! dad) {
5389                        ret = -1; /* in case dad was null */
5390                }
5391                else {
5392                        ret = 0;
5393                }
5394
5395                talloc_destroy(ctx);
5396                return ret;
5397        }
5398
5399        /*
5400         * Are they asking to set an access control element or to set
5401         * the entire access control list?
5402         */
5403        if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5404            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5405            StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5406            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5407            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5408
5409                /* Yup. */
5410                char *namevalue =
5411                        talloc_asprintf(ctx, "%s:%s",
5412                                        name+19, (const char *) value);
5413
5414                if (! ipc_srv) {
5415                        ret = -1; /* errno set by smbc_server() */
5416                }
5417                else if (! namevalue) {
5418                        errno = ENOMEM;
5419                        ret = -1;
5420                } else {
5421                        ret = cacl_set(ctx, srv->cli,
5422                                       ipc_srv->cli, &pol, path,
5423                                       namevalue,
5424                                       (*namevalue == '*'
5425                                        ? SMBC_XATTR_MODE_SET
5426                                        : SMBC_XATTR_MODE_ADD),
5427                                       flags);
5428                }
5429                talloc_destroy(ctx);
5430                return ret;
5431        }
5432
5433        /*
5434         * Are they asking to set the owner?
5435         */
5436        if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5437            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5438
5439                /* Yup. */
5440                char *namevalue =
5441                        talloc_asprintf(ctx, "%s:%s",
5442                                        name+19, (const char *) value);
5443
5444                if (! ipc_srv) {
5445                       
5446                        ret = -1; /* errno set by smbc_server() */
5447                }
5448                else if (! namevalue) {
5449                        errno = ENOMEM;
5450                        ret = -1;
5451                } else {
5452                        ret = cacl_set(ctx, srv->cli,
5453                                       ipc_srv->cli, &pol, path,
5454                                       namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5455                }
5456                talloc_destroy(ctx);
5457                return ret;
5458        }
5459
5460        /*
5461         * Are they asking to set the group?
5462         */
5463        if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5464            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
5465
5466                /* Yup. */
5467                char *namevalue =
5468                        talloc_asprintf(ctx, "%s:%s",
5469                                        name+19, (const char *) value);
5470
5471                if (! ipc_srv) {
5472                        /* errno set by smbc_server() */
5473                        ret = -1;
5474                }
5475                else if (! namevalue) {
5476                        errno = ENOMEM;
5477                        ret = -1;
5478                } else {
5479                        ret = cacl_set(ctx, srv->cli,
5480                                       ipc_srv->cli, &pol, path,
5481                                       namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5482                }
5483                talloc_destroy(ctx);
5484                return ret;
5485        }
5486
5487        /* Determine whether to use old-style or new-style attribute names */
5488        if (context->internal->_full_time_names) {
5489                /* new-style names */
5490                attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5491                attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5492                attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5493                attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5494        } else {
5495                /* old-style names */
5496                attr_strings.create_time_attr = NULL;
5497                attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5498                attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5499                attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5500        }
5501
5502        /*
5503         * Are they asking to set a DOS attribute?
5504         */
5505        if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5506            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5507            (attr_strings.create_time_attr != NULL &&
5508             StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5509            StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5510            StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5511            StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5512
5513                /* get a DOS Attribute Descriptor with current attributes */
5514                dad = dos_attr_query(context, ctx, path, srv);
5515                if (dad) {
5516                        char *namevalue =
5517                                talloc_asprintf(ctx, "%s:%s",
5518                                                name+16, (const char *) value);
5519                        if (! namevalue) {
5520                                errno = ENOMEM;
5521                                ret = -1;
5522                        } else {
5523                                /* Overwrite old with provided new params */
5524                                dos_attr_parse(context, dad, srv, namevalue);
5525
5526                                /* Set the new DOS attributes */
5527                                ret2 = smbc_setatr(context, srv, path,
5528                                                   dad->create_time,
5529                                                   dad->access_time,
5530                                                   dad->write_time,
5531                                                   dad->change_time,
5532                                                   dad->mode);
5533
5534                                /* ret2 has True (success) / False (failure) */
5535                                if (ret2) {
5536                                        ret = 0;
5537                                } else {
5538                                        ret = -1;
5539                                }
5540                        }
5541                } else {
5542                        ret = -1;
5543                }
5544
5545                talloc_destroy(ctx);
5546                return ret;
5547        }
5548
5549        /* Unsupported attribute name */
5550        talloc_destroy(ctx);
5551        errno = EINVAL;
5552        return -1;
5553}
5554
5555static int
5556smbc_getxattr_ctx(SMBCCTX *context,
5557                  const char *fname,
5558                  const char *name,
5559                  const void *value,
5560                  size_t size)
5561{
5562        int ret;
5563        SMBCSRV *srv;
5564        SMBCSRV *ipc_srv;
5565        fstring server;
5566        fstring share;
5567        fstring user;
5568        fstring password;
5569        fstring workgroup;
5570        pstring path;
5571        TALLOC_CTX *ctx;
5572        POLICY_HND pol;
5573        struct {
5574                const char * create_time_attr;
5575                const char * access_time_attr;
5576                const char * write_time_attr;
5577                const char * change_time_attr;
5578        } attr_strings;
5579
5580
5581        if (!context || !context->internal ||
5582            !context->internal->_initialized) {
5583
5584                errno = EINVAL;  /* Best I can think of ... */
5585                return -1;
5586   
5587        }
5588
5589        if (!fname) {
5590
5591                errno = EINVAL;
5592                return -1;
5593
5594        }
5595 
5596        DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5597
5598        if (smbc_parse_path(context, fname,
5599                            workgroup, sizeof(workgroup),
5600                            server, sizeof(server),
5601                            share, sizeof(share),
5602                            path, sizeof(path),
5603                            user, sizeof(user),
5604                            password, sizeof(password),
5605                            NULL, 0)) {
5606                errno = EINVAL;
5607                return -1;
5608        }
5609
5610        if (user[0] == (char)0) fstrcpy(user, context->user);
5611
5612        srv = smbc_server(context, True,
5613                          server, share, workgroup, user, password);
5614        if (!srv) {
5615                return -1;  /* errno set by smbc_server */
5616        }
5617
5618        if (! srv->no_nt_session) {
5619                ipc_srv = smbc_attr_server(context, server, share,
5620                                           workgroup, user, password,
5621                                           &pol);
5622                if (! ipc_srv) {
5623                        srv->no_nt_session = True;
5624                }
5625        } else {
5626                ipc_srv = NULL;
5627        }
5628       
5629        ctx = talloc_init("smbc:getxattr");
5630        if (!ctx) {
5631                errno = ENOMEM;
5632                return -1;
5633        }
5634
5635        /* Determine whether to use old-style or new-style attribute names */
5636        if (context->internal->_full_time_names) {
5637                /* new-style names */
5638                attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5639                attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5640                attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5641                attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5642        } else {
5643                /* old-style names */
5644                attr_strings.create_time_attr = NULL;
5645                attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5646                attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5647                attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5648        }
5649
5650        /* Are they requesting a supported attribute? */
5651        if (StrCaseCmp(name, "system.*") == 0 ||
5652            StrnCaseCmp(name, "system.*!", 9) == 0 ||
5653            StrCaseCmp(name, "system.*+") == 0 ||
5654            StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5655            StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5656            StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5657            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5658            StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5659            StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5660            StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5661            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5662            StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5663            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5664            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5665            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5666            StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5667            StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5668            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5669            StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5670            (attr_strings.create_time_attr != NULL &&
5671             StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5672            StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5673            StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5674            StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
5675            StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5676
5677                /* Yup. */
5678                ret = cacl_get(context, ctx, srv,
5679                               ipc_srv == NULL ? NULL : ipc_srv->cli, 
5680                               &pol, path,
5681                               CONST_DISCARD(char *, name),
5682                               CONST_DISCARD(char *, value), size);
5683                if (ret < 0 && errno == 0) {
5684                        errno = smbc_errno(context, srv->cli);
5685                }
5686                talloc_destroy(ctx);
5687                return ret;
5688        }
5689
5690        /* Unsupported attribute name */
5691        talloc_destroy(ctx);
5692        errno = EINVAL;
5693        return -1;
5694}
5695
5696
5697static int
5698smbc_removexattr_ctx(SMBCCTX *context,
5699                     const char *fname,
5700                     const char *name)
5701{
5702        int ret;
5703        SMBCSRV *srv;
5704        SMBCSRV *ipc_srv;
5705        fstring server;
5706        fstring share;
5707        fstring user;
5708        fstring password;
5709        fstring workgroup;
5710        pstring path;
5711        TALLOC_CTX *ctx;
5712        POLICY_HND pol;
5713
5714        if (!context || !context->internal ||
5715            !context->internal->_initialized) {
5716
5717                errno = EINVAL;  /* Best I can think of ... */
5718                return -1;
5719   
5720        }
5721
5722        if (!fname) {
5723
5724                errno = EINVAL;
5725                return -1;
5726
5727        }
5728 
5729        DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5730
5731        if (smbc_parse_path(context, fname,
5732                            workgroup, sizeof(workgroup),
5733                            server, sizeof(server),
5734                            share, sizeof(share),
5735                            path, sizeof(path),
5736                            user, sizeof(user),
5737                            password, sizeof(password),
5738                            NULL, 0)) {
5739                errno = EINVAL;
5740                return -1;
5741        }
5742
5743        if (user[0] == (char)0) fstrcpy(user, context->user);
5744
5745        srv = smbc_server(context, True,
5746                          server, share, workgroup, user, password);
5747        if (!srv) {
5748                return -1;  /* errno set by smbc_server */
5749        }
5750
5751        if (! srv->no_nt_session) {
5752                ipc_srv = smbc_attr_server(context, server, share,
5753                                           workgroup, user, password,
5754                                           &pol);
5755                srv->no_nt_session = True;
5756        } else {
5757                ipc_srv = NULL;
5758        }
5759       
5760        if (! ipc_srv) {
5761                return -1; /* errno set by smbc_attr_server */
5762        }
5763
5764        ctx = talloc_init("smbc_removexattr");
5765        if (!ctx) {
5766                errno = ENOMEM;
5767                return -1;
5768        }
5769
5770        /* Are they asking to set the entire ACL? */
5771        if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5772            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5773
5774                /* Yup. */
5775                ret = cacl_set(ctx, srv->cli,
5776                               ipc_srv->cli, &pol, path,
5777                               NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5778                talloc_destroy(ctx);
5779                return ret;
5780        }
5781
5782        /*
5783         * Are they asking to remove one or more spceific security descriptor
5784         * attributes?
5785         */
5786        if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5787            StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5788            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5789            StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5790            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5791            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5792            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5793
5794                /* Yup. */
5795                ret = cacl_set(ctx, srv->cli,
5796                               ipc_srv->cli, &pol, path,
5797                               name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5798                talloc_destroy(ctx);
5799                return ret;
5800        }
5801
5802        /* Unsupported attribute name */
5803        talloc_destroy(ctx);
5804        errno = EINVAL;
5805        return -1;
5806}
5807
5808static int
5809smbc_listxattr_ctx(SMBCCTX *context,
5810                   const char *fname,
5811                   char *list,
5812                   size_t size)
5813{
5814        /*
5815         * This isn't quite what listxattr() is supposed to do.  This returns
5816         * the complete set of attribute names, always, rather than only those
5817         * attribute names which actually exist for a file.  Hmmm...
5818         */
5819        const char supported_old[] =
5820                "system.*\0"
5821                "system.*+\0"
5822                "system.nt_sec_desc.revision\0"
5823                "system.nt_sec_desc.owner\0"
5824                "system.nt_sec_desc.owner+\0"
5825                "system.nt_sec_desc.group\0"
5826                "system.nt_sec_desc.group+\0"
5827                "system.nt_sec_desc.acl.*\0"
5828                "system.nt_sec_desc.acl\0"
5829                "system.nt_sec_desc.acl+\0"
5830                "system.nt_sec_desc.*\0"
5831                "system.nt_sec_desc.*+\0"
5832                "system.dos_attr.*\0"
5833                "system.dos_attr.mode\0"
5834                "system.dos_attr.c_time\0"
5835                "system.dos_attr.a_time\0"
5836                "system.dos_attr.m_time\0"
5837                ;
5838        const char supported_new[] =
5839                "system.*\0"
5840                "system.*+\0"
5841                "system.nt_sec_desc.revision\0"
5842                "system.nt_sec_desc.owner\0"
5843                "system.nt_sec_desc.owner+\0"
5844                "system.nt_sec_desc.group\0"
5845                "system.nt_sec_desc.group+\0"
5846                "system.nt_sec_desc.acl.*\0"
5847                "system.nt_sec_desc.acl\0"
5848                "system.nt_sec_desc.acl+\0"
5849                "system.nt_sec_desc.*\0"
5850                "system.nt_sec_desc.*+\0"
5851                "system.dos_attr.*\0"
5852                "system.dos_attr.mode\0"
5853                "system.dos_attr.create_time\0"
5854                "system.dos_attr.access_time\0"
5855                "system.dos_attr.write_time\0"
5856                "system.dos_attr.change_time\0"
5857                ;
5858        const char * supported;
5859
5860        if (context->internal->_full_time_names) {
5861                supported = supported_new;
5862        } else {
5863                supported = supported_old;
5864        }
5865
5866        if (size == 0) {
5867                return sizeof(supported);
5868        }
5869
5870        if (sizeof(supported) > size) {
5871                errno = ERANGE;
5872                return -1;
5873        }
5874
5875        /* this can't be strcpy() because there are embedded null characters */
5876        memcpy(list, supported, sizeof(supported));
5877        return sizeof(supported);
5878}
5879
5880
5881/*
5882 * Open a print file to be written to by other calls
5883 */
5884
5885static SMBCFILE *
5886smbc_open_print_job_ctx(SMBCCTX *context,
5887                        const char *fname)
5888{
5889        fstring server;
5890        fstring share;
5891        fstring user;
5892        fstring password;
5893        pstring path;
5894       
5895        if (!context || !context->internal ||
5896            !context->internal->_initialized) {
5897
5898                errno = EINVAL;
5899                return NULL;
5900   
5901        }
5902
5903        if (!fname) {
5904
5905                errno = EINVAL;
5906                return NULL;
5907
5908        }
5909 
5910        DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5911
5912        if (smbc_parse_path(context, fname,
5913                            NULL, 0,
5914                            server, sizeof(server),
5915                            share, sizeof(share),
5916                            path, sizeof(path),
5917                            user, sizeof(user),
5918                            password, sizeof(password),
5919                            NULL, 0)) {
5920                errno = EINVAL;
5921                return NULL;
5922        }
5923
5924        /* What if the path is empty, or the file exists? */
5925
5926        return context->open(context, fname, O_WRONLY, 666);
5927
5928}
5929
5930/*
5931 * Routine to print a file on a remote server ...
5932 *
5933 * We open the file, which we assume to be on a remote server, and then
5934 * copy it to a print file on the share specified by printq.
5935 */
5936
5937static int
5938smbc_print_file_ctx(SMBCCTX *c_file,
5939                    const char *fname,
5940                    SMBCCTX *c_print,
5941                    const char *printq)
5942{
5943        SMBCFILE *fid1;
5944        SMBCFILE *fid2;
5945        int bytes;
5946        int saverr;
5947        int tot_bytes = 0;
5948        char buf[4096];
5949
5950        if (!c_file || !c_file->internal->_initialized || !c_print ||
5951            !c_print->internal->_initialized) {
5952
5953                errno = EINVAL;
5954                return -1;
5955
5956        }
5957
5958        if (!fname && !printq) {
5959
5960                errno = EINVAL;
5961                return -1;
5962
5963        }
5964
5965        /* Try to open the file for reading ... */
5966
5967        if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
5968               
5969                DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
5970                return -1;  /* smbc_open sets errno */
5971               
5972        }
5973
5974        /* Now, try to open the printer file for writing */
5975
5976        if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
5977
5978                saverr = errno;  /* Save errno */
5979                c_file->close_fn(c_file, fid1);
5980                errno = saverr;
5981                return -1;
5982
5983        }
5984
5985        while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
5986
5987                tot_bytes += bytes;
5988
5989                if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
5990
5991                        saverr = errno;
5992                        c_file->close_fn(c_file, fid1);
5993                        c_print->close_fn(c_print, fid2);
5994                        errno = saverr;
5995
5996                }
5997
5998        }
5999
6000        saverr = errno;
6001
6002        c_file->close_fn(c_file, fid1);  /* We have to close these anyway */
6003        c_print->close_fn(c_print, fid2);
6004
6005        if (bytes < 0) {
6006
6007                errno = saverr;
6008                return -1;
6009
6010        }
6011
6012        return tot_bytes;
6013
6014}
6015
6016/*
6017 * Routine to list print jobs on a printer share ...
6018 */
6019
6020static int
6021smbc_list_print_jobs_ctx(SMBCCTX *context,
6022                         const char *fname,
6023                         smbc_list_print_job_fn fn)
6024{
6025        SMBCSRV *srv;
6026        fstring server;
6027        fstring share;
6028        fstring user;
6029        fstring password;
6030        fstring workgroup;
6031        pstring path;
6032
6033        if (!context || !context->internal ||
6034            !context->internal->_initialized) {
6035
6036                errno = EINVAL;
6037                return -1;
6038
6039        }
6040
6041        if (!fname) {
6042               
6043                errno = EINVAL;
6044                return -1;
6045
6046        }
6047 
6048        DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
6049
6050        if (smbc_parse_path(context, fname,
6051                            workgroup, sizeof(workgroup),
6052                            server, sizeof(server),
6053                            share, sizeof(share),
6054                            path, sizeof(path),
6055                            user, sizeof(user),
6056                            password, sizeof(password),
6057                            NULL, 0)) {
6058                errno = EINVAL;
6059                return -1;
6060        }
6061
6062        if (user[0] == (char)0) fstrcpy(user, context->user);
6063       
6064        srv = smbc_server(context, True,
6065                          server, share, workgroup, user, password);
6066
6067        if (!srv) {
6068
6069                return -1;  /* errno set by smbc_server */
6070
6071        }
6072
6073        if (cli_print_queue(srv->cli,
6074                            (void (*)(struct print_job_info *))fn) < 0) {
6075
6076                errno = smbc_errno(context, srv->cli);
6077                return -1;
6078
6079        }
6080       
6081        return 0;
6082
6083}
6084
6085/*
6086 * Delete a print job from a remote printer share
6087 */
6088
6089static int
6090smbc_unlink_print_job_ctx(SMBCCTX *context,
6091                          const char *fname,
6092                          int id)
6093{
6094        SMBCSRV *srv;
6095        fstring server;
6096        fstring share;
6097        fstring user;
6098        fstring password;
6099        fstring workgroup;
6100        pstring path;
6101        int err;
6102
6103        if (!context || !context->internal ||
6104            !context->internal->_initialized) {
6105
6106                errno = EINVAL;
6107                return -1;
6108
6109        }
6110
6111        if (!fname) {
6112
6113                errno = EINVAL;
6114                return -1;
6115
6116        }
6117 
6118        DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
6119
6120        if (smbc_parse_path(context, fname,
6121                            workgroup, sizeof(workgroup),
6122                            server, sizeof(server),
6123                            share, sizeof(share),
6124                            path, sizeof(path),
6125                            user, sizeof(user),
6126                            password, sizeof(password),
6127                            NULL, 0)) {
6128                errno = EINVAL;
6129                return -1;
6130        }
6131
6132        if (user[0] == (char)0) fstrcpy(user, context->user);
6133
6134        srv = smbc_server(context, True,
6135                          server, share, workgroup, user, password);
6136
6137        if (!srv) {
6138
6139                return -1;  /* errno set by smbc_server */
6140
6141        }
6142
6143        if ((err = cli_printjob_del(srv->cli, id)) != 0) {
6144
6145                if (err < 0)
6146                        errno = smbc_errno(context, srv->cli);
6147                else if (err == ERRnosuchprintjob)
6148                        errno = EINVAL;
6149                return -1;
6150
6151        }
6152
6153        return 0;
6154
6155}
6156
6157/*
6158 * Get a new empty handle to fill in with your own info
6159 */
6160SMBCCTX *
6161smbc_new_context(void)
6162{
6163        SMBCCTX *context;
6164
6165        context = SMB_MALLOC_P(SMBCCTX);
6166        if (!context) {
6167                errno = ENOMEM;
6168                return NULL;
6169        }
6170
6171        ZERO_STRUCTP(context);
6172
6173        context->internal = SMB_MALLOC_P(struct smbc_internal_data);
6174        if (!context->internal) {
6175                SAFE_FREE(context);
6176                errno = ENOMEM;
6177                return NULL;
6178        }
6179
6180        ZERO_STRUCTP(context->internal);
6181
6182       
6183        /* ADD REASONABLE DEFAULTS */
6184        context->debug            = 0;
6185        context->timeout          = 20000; /* 20 seconds */
6186
6187        context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
6188        context->options.urlencode_readdir_entries = False;/* backward compat */
6189        context->options.one_share_per_server      = False;/* backward compat */
6190        context->internal->_share_mode             = SMBC_SHAREMODE_DENY_NONE;
6191                                /* backward compat */
6192
6193        context->open                              = smbc_open_ctx;
6194        context->creat                             = smbc_creat_ctx;
6195        context->read                              = smbc_read_ctx;
6196        context->write                             = smbc_write_ctx;
6197        context->close_fn                          = smbc_close_ctx;
6198        context->unlink                            = smbc_unlink_ctx;
6199        context->rename                            = smbc_rename_ctx;
6200        context->lseek                             = smbc_lseek_ctx;
6201        context->stat                              = smbc_stat_ctx;
6202        context->fstat                             = smbc_fstat_ctx;
6203        context->opendir                           = smbc_opendir_ctx;
6204        context->closedir                          = smbc_closedir_ctx;
6205        context->readdir                           = smbc_readdir_ctx;
6206        context->getdents                          = smbc_getdents_ctx;
6207        context->mkdir                             = smbc_mkdir_ctx;
6208        context->rmdir                             = smbc_rmdir_ctx;
6209        context->telldir                           = smbc_telldir_ctx;
6210        context->lseekdir                          = smbc_lseekdir_ctx;
6211        context->fstatdir                          = smbc_fstatdir_ctx;
6212        context->chmod                             = smbc_chmod_ctx;
6213        context->utimes                            = smbc_utimes_ctx;
6214        context->setxattr                          = smbc_setxattr_ctx;
6215        context->getxattr                          = smbc_getxattr_ctx;
6216        context->removexattr                       = smbc_removexattr_ctx;
6217        context->listxattr                         = smbc_listxattr_ctx;
6218        context->open_print_job                    = smbc_open_print_job_ctx;
6219        context->print_file                        = smbc_print_file_ctx;
6220        context->list_print_jobs                   = smbc_list_print_jobs_ctx;
6221        context->unlink_print_job                  = smbc_unlink_print_job_ctx;
6222
6223        context->callbacks.check_server_fn         = smbc_check_server;
6224        context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
6225
6226        smbc_default_cache_functions(context);
6227
6228        return context;
6229}
6230
6231/*
6232 * Free a context
6233 *
6234 * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
6235 * and thus you'll be leaking memory if not handled properly.
6236 *
6237 */
6238int
6239smbc_free_context(SMBCCTX *context,
6240                  int shutdown_ctx)
6241{
6242        if (!context) {
6243                errno = EBADF;
6244                return 1;
6245        }
6246       
6247        if (shutdown_ctx) {
6248                SMBCFILE * f;
6249                DEBUG(1,("Performing aggressive shutdown.\n"));
6250               
6251                f = context->internal->_files;
6252                while (f) {
6253                        context->close_fn(context, f);
6254                        f = f->next;