libsmb/libsmbclient.c

説明を見る。
00001 /* 
00002    Unix SMB/Netbios implementation.
00003    SMB client library implementation
00004    Copyright (C) Andrew Tridgell 1998
00005    Copyright (C) Richard Sharpe 2000, 2002
00006    Copyright (C) John Terpstra 2000
00007    Copyright (C) Tom Jansen (Ninja ISD) 2002 
00008    Copyright (C) Derrell Lipman 2003, 2004
00009    
00010    This program is free software; you can redistribute it and/or modify
00011    it under the terms of the GNU General Public License as published by
00012    the Free Software Foundation; either version 2 of the License, or
00013    (at your option) any later version.
00014    
00015    This program is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018    GNU General Public License for more details.
00019    
00020    You should have received a copy of the GNU General Public License
00021    along with this program; if not, write to the Free Software
00022    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023 */
00024 
00025 #include "includes.h"
00026 
00027 #include "include/libsmb_internal.h"
00028 
00029 struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir);
00030 struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
00031                                          struct smbc_dirent *dirent);
00032 
00033 /*
00034  * DOS Attribute values (used internally)
00035  */
00036 typedef struct DOS_ATTR_DESC {
00037         int mode;
00038         SMB_OFF_T size;
00039         time_t create_time;
00040         time_t access_time;
00041         time_t write_time;
00042         time_t change_time;
00043         SMB_INO_T inode;
00044 } DOS_ATTR_DESC;
00045 
00046 
00047 /*
00048  * Internal flags for extended attributes
00049  */
00050 
00051 /* internal mode values */
00052 #define SMBC_XATTR_MODE_ADD          1
00053 #define SMBC_XATTR_MODE_REMOVE       2
00054 #define SMBC_XATTR_MODE_REMOVE_ALL   3
00055 #define SMBC_XATTR_MODE_SET          4
00056 #define SMBC_XATTR_MODE_CHOWN        5
00057 #define SMBC_XATTR_MODE_CHGRP        6
00058 
00059 #define CREATE_ACCESS_READ      READ_CONTROL_ACCESS
00060 
00061 /*We should test for this in configure ... */
00062 #ifndef ENOTSUP
00063 #define ENOTSUP EOPNOTSUPP
00064 #endif
00065 
00066 /*
00067  * Functions exported by libsmb_cache.c that we need here
00068  */
00069 int smbc_default_cache_functions(SMBCCTX *context);
00070 
00071 /* 
00072  * check if an element is part of the list. 
00073  * FIXME: Does not belong here !  
00074  * Can anyone put this in a macro in dlinklist.h ?
00075  * -- Tom
00076  */
00077 static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
00078         if (!p || !list) return False;
00079         do {
00080                 if (p == list) return True;
00081                 list = list->next;
00082         } while (list);
00083         return False;
00084 }
00085 
00086 /*
00087  * Find an lsa pipe handle associated with a cli struct.
00088  */
00089 static struct rpc_pipe_client *
00090 find_lsa_pipe_hnd(struct cli_state *ipc_cli)
00091 {
00092         struct rpc_pipe_client *pipe_hnd;
00093 
00094         for (pipe_hnd = ipc_cli->pipe_list;
00095              pipe_hnd;
00096              pipe_hnd = pipe_hnd->next) {
00097             
00098                 if (pipe_hnd->pipe_idx == PI_LSARPC) {
00099                         return pipe_hnd;
00100                 }
00101         }
00102 
00103         return NULL;
00104 }
00105 
00106 static int
00107 smbc_close_ctx(SMBCCTX *context,
00108                SMBCFILE *file);
00109 static off_t
00110 smbc_lseek_ctx(SMBCCTX *context,
00111                SMBCFILE *file,
00112                off_t offset,
00113                int whence);
00114 
00115 extern BOOL in_client;
00116 
00117 /*
00118  * Is the logging working / configfile read ? 
00119  */
00120 static int smbc_initialized = 0;
00121 
00122 static int 
00123 hex2int( unsigned int _char )
00124 {
00125     if ( _char >= 'A' && _char <='F')
00126         return _char - 'A' + 10;
00127     if ( _char >= 'a' && _char <='f')
00128         return _char - 'a' + 10;
00129     if ( _char >= '0' && _char <='9')
00130         return _char - '0';
00131     return -1;
00132 }
00133 
00134 /*
00135  * smbc_urldecode()
00136  *
00137  * Convert strings of %xx to their single character equivalent.  Each 'x' must
00138  * be a valid hexadecimal digit, or that % sequence is left undecoded.
00139  *
00140  * dest may, but need not be, the same pointer as src.
00141  *
00142  * Returns the number of % sequences which could not be converted due to lack
00143  * of two following hexadecimal digits.
00144  */
00145 int
00146 smbc_urldecode(char *dest, char * src, size_t max_dest_len)
00147 {
00148         int old_length = strlen(src);
00149         int i = 0;
00150         int err_count = 0;
00151         pstring temp;
00152         char * p;
00153 
00154         if ( old_length == 0 ) {
00155                 return 0;
00156         }
00157 
00158         p = temp;
00159         while ( i < old_length ) {
00160                 unsigned char character = src[ i++ ];
00161 
00162                 if (character == '%') {
00163                         int a = i+1 < old_length ? hex2int( src[i] ) : -1;
00164                         int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
00165 
00166                         /* Replace valid sequence */
00167                         if (a != -1 && b != -1) {
00168 
00169                                 /* Replace valid %xx sequence with %dd */
00170                                 character = (a * 16) + b;
00171 
00172                                 if (character == '\0') {
00173                                         break; /* Stop at %00 */
00174                                 }
00175 
00176                                 i += 2;
00177                         } else {
00178 
00179                                 err_count++;
00180                         }
00181                 }
00182 
00183                 *p++ = character;
00184         }
00185 
00186         *p = '\0';
00187 
00188         strncpy(dest, temp, max_dest_len - 1);
00189         dest[max_dest_len - 1] = '\0';
00190 
00191         return err_count;
00192 }
00193 
00194 /*
00195  * smbc_urlencode()
00196  *
00197  * Convert any characters not specifically allowed in a URL into their %xx
00198  * equivalent.
00199  *
00200  * Returns the remaining buffer length.
00201  */
00202 int
00203 smbc_urlencode(char * dest, char * src, int max_dest_len)
00204 {
00205         char hex[] = "0123456789ABCDEF";
00206 
00207         for (; *src != '\0' && max_dest_len >= 3; src++) {
00208 
00209                 if ((*src < '0' &&
00210                      *src != '-' &&
00211                      *src != '.') ||
00212                     (*src > '9' &&
00213                      *src < 'A') ||
00214                     (*src > 'Z' &&
00215                      *src < 'a' &&
00216                      *src != '_') ||
00217                     (*src > 'z')) {
00218                         *dest++ = '%';
00219                         *dest++ = hex[(*src >> 4) & 0x0f];
00220                         *dest++ = hex[*src & 0x0f];
00221                         max_dest_len -= 3;
00222                 } else {
00223                         *dest++ = *src;
00224                         max_dest_len--;
00225                 }
00226         }
00227 
00228         *dest++ = '\0';
00229         max_dest_len--;
00230         
00231         return max_dest_len;
00232 }
00233 
00234 /*
00235  * Function to parse a path and turn it into components
00236  *
00237  * The general format of an SMB URI is explain in Christopher Hertel's CIFS
00238  * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
00239  * general format ("smb:" only; we do not look for "cifs:").
00240  *
00241  *
00242  * We accept:
00243  *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
00244  *
00245  * Meaning of URLs:
00246  *
00247  * smb://           Show all workgroups.
00248  *
00249  *                  The method of locating the list of workgroups varies
00250  *                  depending upon the setting of the context variable
00251  *                  context->options.browse_max_lmb_count.  This value
00252  *                  determine the maximum number of local master browsers to
00253  *                  query for the list of workgroups.  In order to ensure that
00254  *                  a complete list of workgroups is obtained, all master
00255  *                  browsers must be queried, but if there are many
00256  *                  workgroups, the time spent querying can begin to add up.
00257  *                  For small networks (not many workgroups), it is suggested
00258  *                  that this variable be set to 0, indicating query all local
00259  *                  master browsers.  When the network has many workgroups, a
00260  *                  reasonable setting for this variable might be around 3.
00261  *
00262  * smb://name/      if name<1D> or name<1B> exists, list servers in
00263  *                  workgroup, else, if name<20> exists, list all shares
00264  *                  for server ...
00265  *
00266  * If "options" are provided, this function returns the entire option list as a
00267  * string, for later parsing by the caller.  Note that currently, no options
00268  * are supported.
00269  */
00270 
00271 static const char *smbc_prefix = "smb:";
00272 
00273 static int
00274 smbc_parse_path(SMBCCTX *context,
00275                 const char *fname,
00276                 char *workgroup, int workgroup_len,
00277                 char *server, int server_len,
00278                 char *share, int share_len,
00279                 char *path, int path_len,
00280                 char *user, int user_len,
00281                 char *password, int password_len,
00282                 char *options, int options_len)
00283 {
00284         static pstring s;
00285         pstring userinfo;
00286         const char *p;
00287         char *q, *r;
00288         int len;
00289 
00290         server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
00291 
00292         /*
00293          * Assume we wont find an authentication domain to parse, so default
00294          * to the workgroup in the provided context.
00295          */
00296         if (workgroup != NULL) {
00297                 strncpy(workgroup, context->workgroup, workgroup_len - 1);
00298                 workgroup[workgroup_len - 1] = '\0';
00299         }
00300 
00301         if (options != NULL && options_len > 0) {
00302                 options[0] = (char)0;
00303         }
00304         pstrcpy(s, fname);
00305 
00306         /* see if it has the right prefix */
00307         len = strlen(smbc_prefix);
00308         if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
00309                 return -1; /* What about no smb: ? */
00310         }
00311 
00312         p = s + len;
00313 
00314         /* Watch the test below, we are testing to see if we should exit */
00315 
00316         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
00317 
00318                 DEBUG(1, ("Invalid path (does not begin with smb://"));
00319                 return -1;
00320 
00321         }
00322 
00323         p += 2;  /* Skip the double slash */
00324 
00325         /* See if any options were specified */
00326         if ((q = strrchr(p, '?')) != NULL ) {
00327                 /* There are options.  Null terminate here and point to them */
00328                 *q++ = '\0';
00329                 
00330                 DEBUG(4, ("Found options '%s'", q));
00331 
00332                 /* Copy the options */
00333                 if (options != NULL && options_len > 0) {
00334                         safe_strcpy(options, q, options_len - 1);
00335                 }
00336         }
00337 
00338         if (*p == (char)0)
00339             goto decoding;
00340 
00341         if (*p == '/') {
00342                 int wl = strlen(context->workgroup);
00343 
00344                 if (wl > 16) {
00345                         wl = 16;
00346                 }
00347 
00348                 strncpy(server, context->workgroup, wl);
00349                 server[wl] = '\0';
00350                 return 0;
00351         }
00352 
00353         /*
00354          * ok, its for us. Now parse out the server, share etc. 
00355          *
00356          * However, we want to parse out [[domain;]user[:password]@] if it
00357          * exists ...
00358          */
00359 
00360         /* check that '@' occurs before '/', if '/' exists at all */
00361         q = strchr_m(p, '@');
00362         r = strchr_m(p, '/');
00363         if (q && (!r || q < r)) {
00364                 pstring username, passwd, domain;
00365                 const char *u = userinfo;
00366 
00367                 next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
00368 
00369                 username[0] = passwd[0] = domain[0] = 0;
00370 
00371                 if (strchr_m(u, ';')) {
00372       
00373                         next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
00374 
00375                 }
00376 
00377                 if (strchr_m(u, ':')) {
00378 
00379                         next_token_no_ltrim(&u, username, ":", sizeof(fstring));
00380 
00381                         pstrcpy(passwd, u);
00382 
00383                 }
00384                 else {
00385 
00386                         pstrcpy(username, u);
00387 
00388                 }
00389 
00390                 if (domain[0] && workgroup) {
00391                         strncpy(workgroup, domain, workgroup_len - 1);
00392                         workgroup[workgroup_len - 1] = '\0';
00393                 }
00394 
00395                 if (username[0]) {
00396                         strncpy(user, username, user_len - 1);
00397                         user[user_len - 1] = '\0';
00398                 }
00399 
00400                 if (passwd[0]) {
00401                         strncpy(password, passwd, password_len - 1);
00402                         password[password_len - 1] = '\0';
00403                 }
00404 
00405         }
00406 
00407         if (!next_token(&p, server, "/", sizeof(fstring))) {
00408 
00409                 return -1;
00410 
00411         }
00412 
00413         if (*p == (char)0) goto decoding;  /* That's it ... */
00414   
00415         if (!next_token(&p, share, "/", sizeof(fstring))) {
00416 
00417                 return -1;
00418 
00419         }
00420 
00421         /*
00422          * Prepend a leading slash if there's a file path, as required by
00423          * NetApp filers.
00424          */
00425         *path = '\0';
00426         if (*p != '\0') {
00427                 *path = '/';
00428                 safe_strcpy(path + 1, p, path_len - 2);
00429         }
00430 
00431         all_string_sub(path, "/", "\\", 0);
00432 
00433  decoding:
00434         (void) smbc_urldecode(path, path, path_len);
00435         (void) smbc_urldecode(server, server, server_len);
00436         (void) smbc_urldecode(share, share, share_len);
00437         (void) smbc_urldecode(user, user, user_len);
00438         (void) smbc_urldecode(password, password, password_len);
00439 
00440         return 0;
00441 }
00442 
00443 /*
00444  * Verify that the options specified in a URL are valid
00445  */
00446 static int
00447 smbc_check_options(char *server,
00448                    char *share,
00449                    char *path,
00450                    char *options)
00451 {
00452         DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
00453                   "path='%s' options='%s'\n",
00454                   server, share, path, options));
00455 
00456         /* No options at all is always ok */
00457         if (! *options) return 0;
00458 
00459         /* Currently, we don't support any options. */
00460         return -1;
00461 }
00462 
00463 /*
00464  * Convert an SMB error into a UNIX error ...
00465  */
00466 static int
00467 smbc_errno(SMBCCTX *context,
00468            struct cli_state *c)
00469 {
00470         int ret = cli_errno(c);
00471         
00472         if (cli_is_dos_error(c)) {
00473                 uint8 eclass;
00474                 uint32 ecode;
00475 
00476                 cli_dos_error(c, &eclass, &ecode);
00477                 
00478                 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", 
00479                          (int)eclass, (int)ecode, (int)ecode, ret));
00480         } else {
00481                 NTSTATUS status;
00482 
00483                 status = cli_nt_error(c);
00484 
00485                 DEBUG(3,("smbc errno %s -> %d\n",
00486                          nt_errstr(status), ret));
00487         }
00488 
00489         return ret;
00490 }
00491 
00492 /* 
00493  * Check a server for being alive and well.
00494  * returns 0 if the server is in shape. Returns 1 on error 
00495  * 
00496  * Also useable outside libsmbclient to enable external cache
00497  * to do some checks too.
00498  */
00499 static int
00500 smbc_check_server(SMBCCTX * context,
00501                   SMBCSRV * server) 
00502 {
00503         socklen_t size;
00504         struct sockaddr addr;
00505 
00506         size = sizeof(addr);
00507         return (getpeername(server->cli->fd, &addr, &size) == -1);
00508 }
00509 
00510 /* 
00511  * Remove a server from the cached server list it's unused.
00512  * On success, 0 is returned. 1 is returned if the server could not be removed.
00513  * 
00514  * Also useable outside libsmbclient
00515  */
00516 int
00517 smbc_remove_unused_server(SMBCCTX * context,
00518                           SMBCSRV * srv)
00519 {
00520         SMBCFILE * file;
00521 
00522         /* are we being fooled ? */
00523         if (!context || !context->internal ||
00524             !context->internal->_initialized || !srv) return 1;
00525 
00526         
00527         /* Check all open files/directories for a relation with this server */
00528         for (file = context->internal->_files; file; file=file->next) {
00529                 if (file->srv == srv) {
00530                         /* Still used */
00531                         DEBUG(3, ("smbc_remove_usused_server: "
00532                                   "%p still used by %p.\n", 
00533                                   srv, file));
00534                         return 1;
00535                 }
00536         }
00537 
00538         DLIST_REMOVE(context->internal->_servers, srv);
00539 
00540         cli_shutdown(srv->cli);
00541         srv->cli = NULL;
00542 
00543         DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
00544 
00545         (context->callbacks.remove_cached_srv_fn)(context, srv);
00546 
00547         SAFE_FREE(srv);
00548         
00549         return 0;
00550 }
00551 
00552 static SMBCSRV *
00553 find_server(SMBCCTX *context,
00554             const char *server,
00555             const char *share,
00556             fstring workgroup,
00557             fstring username,
00558             fstring password)
00559 {
00560         SMBCSRV *srv;
00561         int auth_called = 0;
00562         
00563  check_server_cache:
00564 
00565         srv = (context->callbacks.get_cached_srv_fn)(context, server, share, 
00566                                                      workgroup, username);
00567 
00568         if (!auth_called && !srv && (!username[0] || !password[0])) {
00569                 if (context->internal->_auth_fn_with_context != NULL) {
00570                         (context->internal->_auth_fn_with_context)(
00571                                 context,
00572                                 server, share,
00573                                 workgroup, sizeof(fstring),
00574                                 username, sizeof(fstring),
00575                                 password, sizeof(fstring));
00576                 } else {
00577                         (context->callbacks.auth_fn)(
00578                                 server, share,
00579                                 workgroup, sizeof(fstring),
00580                                 username, sizeof(fstring),
00581                                 password, sizeof(fstring));
00582                 }
00583 
00584                 /*
00585                  * However, smbc_auth_fn may have picked up info relating to
00586                  * an existing connection, so try for an existing connection
00587                  * again ...
00588                  */
00589                 auth_called = 1;
00590                 goto check_server_cache;
00591                 
00592         }
00593         
00594         if (srv) {
00595                 if ((context->callbacks.check_server_fn)(context, srv)) {
00596                         /*
00597                          * This server is no good anymore 
00598                          * Try to remove it and check for more possible
00599                          * servers in the cache
00600                          */
00601                         if ((context->callbacks.remove_unused_server_fn)(context,
00602                                                                          srv)) { 
00603                                 /*
00604                                  * We could not remove the server completely,
00605                                  * remove it from the cache so we will not get
00606                                  * it again. It will be removed when the last
00607                                  * file/dir is closed.
00608                                  */
00609                                 (context->callbacks.remove_cached_srv_fn)(context,
00610                                                                           srv);
00611                         }
00612                         
00613                         /*
00614                          * Maybe there are more cached connections to this
00615                          * server
00616                          */
00617                         goto check_server_cache; 
00618                 }
00619 
00620                 return srv;
00621         }
00622 
00623         return NULL;
00624 }
00625 
00626 /*
00627  * Connect to a server, possibly on an existing connection
00628  *
00629  * Here, what we want to do is: If the server and username
00630  * match an existing connection, reuse that, otherwise, establish a 
00631  * new connection.
00632  *
00633  * If we have to create a new connection, call the auth_fn to get the
00634  * info we need, unless the username and password were passed in.
00635  */
00636 
00637 static SMBCSRV *
00638 smbc_server(SMBCCTX *context,
00639             BOOL connect_if_not_found,
00640             const char *server,
00641             const char *share, 
00642             fstring workgroup,
00643             fstring username, 
00644             fstring password)
00645 {
00646         SMBCSRV *srv=NULL;
00647         struct cli_state *c;
00648         struct nmb_name called, calling;
00649         const char *server_n = server;
00650         pstring ipenv;
00651         struct in_addr ip;
00652         int tried_reverse = 0;
00653         int port_try_first;
00654         int port_try_next;
00655         const char *username_used;
00656         NTSTATUS status;
00657 
00658         zero_ip(&ip);
00659         ZERO_STRUCT(c);
00660 
00661         if (server[0] == 0) {
00662                 errno = EPERM;
00663                 return NULL;
00664         }
00665 
00666         /* Look for a cached connection */
00667         srv = find_server(context, server, share,
00668                           workgroup, username, password);
00669         
00670         /*
00671          * If we found a connection and we're only allowed one share per
00672          * server...
00673          */
00674         if (srv && *share != '\0' && context->options.one_share_per_server) {
00675 
00676                 /*
00677                  * ... then if there's no current connection to the share,
00678                  * connect to it.  find_server(), or rather the function
00679                  * pointed to by context->callbacks.get_cached_srv_fn which
00680                  * was called by find_server(), will have issued a tree
00681                  * disconnect if the requested share is not the same as the
00682                  * one that was already connected.
00683                  */
00684                 if (srv->cli->cnum == (uint16) -1) {
00685                         /* Ensure we have accurate auth info */
00686                         if (context->internal->_auth_fn_with_context != NULL) {
00687                                 (context->internal->_auth_fn_with_context)(
00688                                         context,
00689                                         server, share,
00690                                         workgroup, sizeof(fstring),
00691                                         username, sizeof(fstring),
00692                                         password, sizeof(fstring));
00693                         } else {
00694                                 (context->callbacks.auth_fn)(
00695                                         server, share,
00696                                         workgroup, sizeof(fstring),
00697                                         username, sizeof(fstring),
00698                                         password, sizeof(fstring));
00699                         }
00700 
00701                         if (! cli_send_tconX(srv->cli, share, "?????",
00702                                              password, strlen(password)+1)) {
00703                         
00704                                 errno = smbc_errno(context, srv->cli);
00705                                 cli_shutdown(srv->cli);
00706                                 srv->cli = NULL;
00707                                 (context->callbacks.remove_cached_srv_fn)(context,
00708                                                                           srv);
00709                                 srv = NULL;
00710                         }
00711 
00712                         /*
00713                          * Regenerate the dev value since it's based on both
00714                          * server and share
00715                          */
00716                         if (srv) {
00717                                 srv->dev = (dev_t)(str_checksum(server) ^
00718                                                    str_checksum(share));
00719                         }
00720                 }
00721         }
00722         
00723         /* If we have a connection... */
00724         if (srv) {
00725 
00726                 /* ... then we're done here.  Give 'em what they came for. */
00727                 return srv;
00728         }
00729 
00730         /* If we're not asked to connect when a connection doesn't exist... */
00731         if (! connect_if_not_found) {
00732                 /* ... then we're done here. */
00733                 return NULL;
00734         }
00735 
00736         make_nmb_name(&calling, context->netbios_name, 0x0);
00737         make_nmb_name(&called , server, 0x20);
00738 
00739         DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
00740   
00741         DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
00742 
00743  again:
00744         slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
00745 
00746         zero_ip(&ip);
00747 
00748         /* have to open a new connection */
00749         if ((c = cli_initialise()) == NULL) {
00750                 errno = ENOMEM;
00751                 return NULL;
00752         }
00753 
00754         if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
00755                 c->use_kerberos = True;
00756         }
00757         if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
00758                 c->fallback_after_kerberos = True;
00759         }
00760 
00761         c->timeout = context->timeout;
00762 
00763         /*
00764          * Force use of port 139 for first try if share is $IPC, empty, or
00765          * null, so browse lists can work
00766          */
00767         if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
00768                 port_try_first = 139;
00769                 port_try_next = 445;
00770         } else {
00771                 port_try_first = 445;
00772                 port_try_next = 139;
00773         }
00774 
00775         c->port = port_try_first;
00776 
00777         status = cli_connect(c, server_n, &ip);
00778         if (!NT_STATUS_IS_OK(status)) {
00779 
00780                 /* First connection attempt failed.  Try alternate port. */
00781                 c->port = port_try_next;
00782 
00783                 status = cli_connect(c, server_n, &ip);
00784                 if (!NT_STATUS_IS_OK(status)) {
00785                         cli_shutdown(c);
00786                         errno = ETIMEDOUT;
00787                         return NULL;
00788                 }
00789         }
00790 
00791         if (!cli_session_request(c, &calling, &called)) {
00792                 cli_shutdown(c);
00793                 if (strcmp(called.name, "*SMBSERVER")) {
00794                         make_nmb_name(&called , "*SMBSERVER", 0x20);
00795                         goto again;
00796                 } else {  /* Try one more time, but ensure we don't loop */
00797 
00798                         /* Only try this if server is an IP address ... */
00799 
00800                         if (is_ipaddress(server) && !tried_reverse) {
00801                                 fstring remote_name;
00802                                 struct in_addr rem_ip;
00803 
00804                                 if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
00805                                         DEBUG(4, ("Could not convert IP address "
00806                                                 "%s to struct in_addr\n", server));
00807                                         errno = ETIMEDOUT;
00808                                         return NULL;
00809                                 }
00810 
00811                                 tried_reverse++; /* Yuck */
00812 
00813                                 if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
00814                                         make_nmb_name(&called, remote_name, 0x20);
00815                                         goto again;
00816                                 }
00817                         }
00818                 }
00819                 errno = ETIMEDOUT;
00820                 return NULL;
00821         }
00822   
00823         DEBUG(4,(" session request ok\n"));
00824   
00825         if (!cli_negprot(c)) {
00826                 cli_shutdown(c);
00827                 errno = ETIMEDOUT;
00828                 return NULL;
00829         }
00830 
00831         username_used = username;
00832 
00833         if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, 
00834                                                password, strlen(password),
00835                                                password, strlen(password),
00836                                                workgroup))) {
00837                 
00838                 /* Failed.  Try an anonymous login, if allowed by flags. */
00839                 username_used = "";
00840 
00841                 if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
00842                      !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
00843                                                         password, 1,
00844                                                         password, 0,
00845                                                         workgroup))) {
00846 
00847                         cli_shutdown(c);
00848                         errno = EPERM;
00849                         return NULL;
00850                 }
00851         }
00852 
00853         DEBUG(4,(" session setup ok\n"));
00854 
00855         if (!cli_send_tconX(c, share, "?????",
00856                             password, strlen(password)+1)) {
00857                 errno = smbc_errno(context, c);
00858                 cli_shutdown(c);
00859                 return NULL;
00860         }
00861   
00862         DEBUG(4,(" tconx ok\n"));
00863   
00864         /*
00865          * Ok, we have got a nice connection
00866          * Let's allocate a server structure.
00867          */
00868 
00869         srv = SMB_MALLOC_P(SMBCSRV);
00870         if (!srv) {
00871                 errno = ENOMEM;
00872                 goto failed;
00873         }
00874 
00875         ZERO_STRUCTP(srv);
00876         srv->cli = c;
00877         srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
00878         srv->no_pathinfo = False;
00879         srv->no_pathinfo2 = False;
00880         srv->no_nt_session = False;
00881 
00882         /* now add it to the cache (internal or external)  */
00883         /* Let the cache function set errno if it wants to */
00884         errno = 0;
00885         if ((context->callbacks.add_cached_srv_fn)(context, srv,
00886                                                    server, share,
00887                                                    workgroup, username)) {
00888                 int saved_errno = errno;
00889                 DEBUG(3, (" Failed to add server to cache\n"));
00890                 errno = saved_errno;
00891                 if (errno == 0) {
00892                         errno = ENOMEM;
00893                 }
00894                 goto failed;
00895         }
00896         
00897         DEBUG(2, ("Server connect ok: //%s/%s: %p\n", 
00898                   server, share, srv));
00899 
00900         DLIST_ADD(context->internal->_servers, srv);
00901         return srv;
00902 
00903  failed:
00904         cli_shutdown(c);
00905         if (!srv) {
00906                 return NULL;
00907         }
00908   
00909         SAFE_FREE(srv);
00910         return NULL;
00911 }
00912 
00913 /*
00914  * Connect to a server for getting/setting attributes, possibly on an existing
00915  * connection.  This works similarly to smbc_server().
00916  */
00917 static SMBCSRV *
00918 smbc_attr_server(SMBCCTX *context,
00919                  const char *server,
00920                  const char *share, 
00921                  fstring workgroup,
00922                  fstring username,
00923                  fstring password,
00924                  POLICY_HND *pol)
00925 {
00926         int flags;
00927         struct in_addr ip;
00928         struct cli_state *ipc_cli;
00929         struct rpc_pipe_client *pipe_hnd;
00930         NTSTATUS nt_status;
00931         SMBCSRV *ipc_srv=NULL;
00932 
00933         /*
00934          * See if we've already created this special connection.  Reference
00935          * our "special" share name '*IPC$', which is an impossible real share
00936          * name due to the leading asterisk.
00937          */
00938         ipc_srv = find_server(context, server, "*IPC$",
00939                               workgroup, username, password);
00940         if (!ipc_srv) {
00941 
00942                 /* We didn't find a cached connection.  Get the password */
00943                 if (*password == '\0') {
00944                         /* ... then retrieve it now. */
00945                         if (context->internal->_auth_fn_with_context != NULL) {
00946                                 (context->internal->_auth_fn_with_context)(
00947                                         context,
00948                                         server, share,
00949                                         workgroup, sizeof(fstring),
00950                                         username, sizeof(fstring),
00951                                         password, sizeof(fstring));
00952                         } else {
00953                                 (context->callbacks.auth_fn)(
00954                                         server, share,
00955                                         workgroup, sizeof(fstring),
00956                                         username, sizeof(fstring),
00957                                         password, sizeof(fstring));
00958                         }
00959                 }
00960         
00961                 flags = 0;
00962                 if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
00963                         flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
00964                 }
00965 
00966                 zero_ip(&ip);
00967                 nt_status = cli_full_connection(&ipc_cli,
00968                                                 global_myname(), server, 
00969                                                 &ip, 0, "IPC$", "?????",  
00970                                                 username, workgroup,
00971                                                 password, flags,
00972                                                 Undefined, NULL);
00973                 if (! NT_STATUS_IS_OK(nt_status)) {
00974                         DEBUG(1,("cli_full_connection failed! (%s)\n",
00975                                  nt_errstr(nt_status)));
00976                         errno = ENOTSUP;
00977                         return NULL;
00978                 }
00979 
00980                 ipc_srv = SMB_MALLOC_P(SMBCSRV);
00981                 if (!ipc_srv) {
00982                         errno = ENOMEM;
00983                         cli_shutdown(ipc_cli);
00984                         return NULL;
00985                 }
00986 
00987                 ZERO_STRUCTP(ipc_srv);
00988                 ipc_srv->cli = ipc_cli;
00989 
00990                 if (pol) {
00991                         pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
00992                                                             PI_LSARPC,
00993                                                             &nt_status);
00994                         if (!pipe_hnd) {
00995                                 DEBUG(1, ("cli_nt_session_open fail!\n"));
00996                                 errno = ENOTSUP;
00997                                 cli_shutdown(ipc_srv->cli);
00998                                 free(ipc_srv);
00999                                 return NULL;
01000                         }
01001 
01002                         /*
01003                          * Some systems don't support
01004                          * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
01005                          * so we might as well do it too.
01006                          */
01007         
01008                         nt_status = rpccli_lsa_open_policy(
01009                                 pipe_hnd,
01010                                 ipc_srv->cli->mem_ctx,
01011                                 True, 
01012                                 GENERIC_EXECUTE_ACCESS,
01013                                 pol);
01014         
01015                         if (!NT_STATUS_IS_OK(nt_status)) {
01016                                 errno = smbc_errno(context, ipc_srv->cli);
01017                                 cli_shutdown(ipc_srv->cli);
01018                                 return NULL;
01019                         }
01020                 }
01021 
01022                 /* now add it to the cache (internal or external) */
01023 
01024                 errno = 0;      /* let cache function set errno if it likes */
01025                 if ((context->callbacks.add_cached_srv_fn)(context, ipc_srv,
01026                                                            server,
01027                                                            "*IPC$",
01028                                                            workgroup,
01029                                                            username)) {
01030                         DEBUG(3, (" Failed to add server to cache\n"));
01031                         if (errno == 0) {
01032                                 errno = ENOMEM;
01033                         }
01034                         cli_shutdown(ipc_srv->cli);
01035                         free(ipc_srv);
01036                         return NULL;
01037                 }
01038 
01039                 DLIST_ADD(context->internal->_servers, ipc_srv);
01040         }
01041 
01042         return ipc_srv;
01043 }
01044 
01045 /*
01046  * Routine to open() a file ...
01047  */
01048 
01049 static SMBCFILE *
01050 smbc_open_ctx(SMBCCTX *context,
01051               const char *fname,
01052               int flags,
01053               mode_t mode)
01054 {
01055         fstring server, share, user, password, workgroup;
01056         pstring path;
01057         pstring targetpath;
01058         struct cli_state *targetcli;
01059         SMBCSRV *srv   = NULL;
01060         SMBCFILE *file = NULL;
01061         int fd;
01062 
01063         if (!context || !context->internal ||
01064             !context->internal->_initialized) {
01065 
01066                 errno = EINVAL;  /* Best I can think of ... */
01067                 return NULL;
01068 
01069         }
01070 
01071         if (!fname) {
01072 
01073                 errno = EINVAL;
01074                 return NULL;
01075 
01076         }
01077 
01078         if (smbc_parse_path(context, fname,
01079                             workgroup, sizeof(workgroup),
01080                             server, sizeof(server),
01081                             share, sizeof(share),
01082                             path, sizeof(path),
01083                             user, sizeof(user),
01084                             password, sizeof(password),
01085                             NULL, 0)) {
01086                 errno = EINVAL;
01087                 return NULL;
01088         }
01089 
01090         if (user[0] == (char)0) fstrcpy(user, context->user);
01091 
01092         srv = smbc_server(context, True,
01093                           server, share, workgroup, user, password);
01094 
01095         if (!srv) {
01096 
01097                 if (errno == EPERM) errno = EACCES;
01098                 return NULL;  /* smbc_server sets errno */
01099     
01100         }
01101 
01102         /* Hmmm, the test for a directory is suspect here ... FIXME */
01103 
01104         if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
01105     
01106                 fd = -1;
01107 
01108         }
01109         else {
01110           
01111                 file = SMB_MALLOC_P(SMBCFILE);
01112 
01113                 if (!file) {
01114 
01115                         errno = ENOMEM;
01116                         return NULL;
01117 
01118                 }
01119 
01120                 ZERO_STRUCTP(file);
01121 
01122                 /*d_printf(">>>open: resolving %s\n", path);*/
01123                 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
01124                 {
01125                         d_printf("Could not resolve %s\n", path);
01126                         SAFE_FREE(file);
01127                         return NULL;
01128                 }
01129                 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
01130                 
01131                 if ((fd = cli_open(targetcli, targetpath, flags,
01132                                    context->internal->_share_mode)) < 0) {
01133 
01134                         /* Handle the error ... */
01135 
01136                         SAFE_FREE(file);
01137                         errno = smbc_errno(context, targetcli);
01138                         return NULL;
01139 
01140                 }
01141 
01142                 /* Fill in file struct */
01143 
01144                 file->cli_fd  = fd;
01145                 file->fname   = SMB_STRDUP(fname);
01146                 file->srv     = srv;
01147                 file->offset  = 0;
01148                 file->file    = True;
01149 
01150                 DLIST_ADD(context->internal->_files, file);
01151 
01152                 /*
01153                  * If the file was opened in O_APPEND mode, all write
01154                  * operations should be appended to the file.  To do that,
01155                  * though, using this protocol, would require a getattrE()
01156                  * call for each and every write, to determine where the end
01157                  * of the file is. (There does not appear to be an append flag
01158                  * in the protocol.)  Rather than add all of that overhead of
01159                  * retrieving the current end-of-file offset prior to each
01160                  * write operation, we'll assume that most append operations
01161                  * will continuously write, so we'll just set the offset to
01162                  * the end of the file now and hope that's adequate.
01163                  *
01164                  * Note to self: If this proves inadequate, and O_APPEND
01165                  * should, in some cases, be forced for each write, add a
01166                  * field in the context options structure, for
01167                  * "strict_append_mode" which would select between the current
01168                  * behavior (if FALSE) or issuing a getattrE() prior to each
01169                  * write and forcing the write to the end of the file (if
01170                  * TRUE).  Adding that capability will likely require adding
01171                  * an "append" flag into the _SMBCFILE structure to track
01172                  * whether a file was opened in O_APPEND mode.  -- djl
01173                  */
01174                 if (flags & O_APPEND) {
01175                         if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
01176                                 (void) smbc_close_ctx(context, file);
01177                                 errno = ENXIO;
01178                                 return NULL;
01179                         }
01180                 }
01181 
01182                 return file;
01183 
01184         }
01185 
01186         /* Check if opendir needed ... */
01187 
01188         if (fd == -1) {
01189                 int eno = 0;
01190 
01191                 eno = smbc_errno(context, srv->cli);
01192                 file = (context->opendir)(context, fname);
01193                 if (!file) errno = eno;
01194                 return file;
01195 
01196         }
01197 
01198         errno = EINVAL; /* FIXME, correct errno ? */
01199         return NULL;
01200 
01201 }
01202 
01203 /*
01204  * Routine to create a file 
01205  */
01206 
01207 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
01208 
01209 static SMBCFILE *
01210 smbc_creat_ctx(SMBCCTX *context,
01211                const char *path,
01212                mode_t mode)
01213 {
01214 
01215         if (!context || !context->internal ||
01216             !context->internal->_initialized) {
01217 
01218                 errno = EINVAL;
01219                 return NULL;
01220 
01221         }
01222 
01223         return smbc_open_ctx(context, path, creat_bits, mode);
01224 }
01225 
01226 /*
01227  * Routine to read() a file ...
01228  */
01229 
01230 static ssize_t
01231 smbc_read_ctx(SMBCCTX *context,
01232               SMBCFILE *file,
01233               void *buf,
01234               size_t count)
01235 {
01236         int ret;
01237         fstring server, share, user, password;
01238         pstring path, targetpath;
01239         struct cli_state *targetcli;
01240 
01241         /*
01242          * offset:
01243          *
01244          * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
01245          * appears to pass file->offset (which is type off_t) differently than
01246          * a local variable of type off_t.  Using local variable "offset" in
01247          * the call to cli_read() instead of file->offset fixes a problem
01248          * retrieving data at an offset greater than 4GB.
01249          */
01250         off_t offset;
01251 
01252         if (!context || !context->internal ||
01253             !context->internal->_initialized) {
01254 
01255                 errno = EINVAL;
01256                 return -1;
01257 
01258         }
01259 
01260         DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
01261 
01262         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
01263 
01264                 errno = EBADF;
01265                 return -1;
01266 
01267         }
01268 
01269         offset = file->offset;
01270 
01271         /* Check that the buffer exists ... */
01272 
01273         if (buf == NULL) {
01274 
01275                 errno = EINVAL;
01276                 return -1;
01277 
01278         }
01279 
01280         /*d_printf(">>>read: parsing %s\n", file->fname);*/
01281         if (smbc_parse_path(context, file->fname,
01282                             NULL, 0,
01283                             server, sizeof(server),
01284                             share, sizeof(share),
01285                             path, sizeof(path),
01286                             user, sizeof(user),
01287                             password, sizeof(password),
01288                             NULL, 0)) {
01289                 errno = EINVAL;
01290                 return -1;
01291         }
01292         
01293         /*d_printf(">>>read: resolving %s\n", path);*/
01294         if (!cli_resolve_path("", file->srv->cli, path,
01295                               &targetcli, targetpath))
01296         {
01297                 d_printf("Could not resolve %s\n", path);
01298                 return -1;
01299         }
01300         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
01301         
01302         ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
01303 
01304         if (ret < 0) {
01305 
01306                 errno = smbc_errno(context, targetcli);
01307                 return -1;
01308 
01309         }
01310 
01311         file->offset += ret;
01312 
01313         DEBUG(4, ("  --> %d\n", ret));
01314 
01315         return ret;  /* Success, ret bytes of data ... */
01316 
01317 }
01318 
01319 /*
01320  * Routine to write() a file ...
01321  */
01322 
01323 static ssize_t
01324 smbc_write_ctx(SMBCCTX *context,
01325                SMBCFILE *file,
01326                void *buf,
01327                size_t count)
01328 {
01329         int ret;
01330         off_t offset;
01331         fstring server, share, user, password;
01332         pstring path, targetpath;
01333         struct cli_state *targetcli;
01334 
01335         /* First check all pointers before dereferencing them */
01336         
01337         if (!context || !context->internal ||
01338             !context->internal->_initialized) {
01339 
01340                 errno = EINVAL;
01341                 return -1;
01342 
01343         }
01344 
01345         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
01346 
01347                 errno = EBADF;
01348                 return -1;
01349     
01350         }
01351 
01352         /* Check that the buffer exists ... */
01353 
01354         if (buf == NULL) {
01355 
01356                 errno = EINVAL;
01357                 return -1;
01358 
01359         }
01360 
01361         offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
01362 
01363         /*d_printf(">>>write: parsing %s\n", file->fname);*/
01364         if (smbc_parse_path(context, file->fname,
01365                             NULL, 0,
01366                             server, sizeof(server),
01367                             share, sizeof(share),
01368                             path, sizeof(path),
01369                             user, sizeof(user),
01370                             password, sizeof(password),
01371                             NULL, 0)) {
01372                 errno = EINVAL;
01373                 return -1;
01374         }
01375         
01376         /*d_printf(">>>write: resolving %s\n", path);*/
01377         if (!cli_resolve_path("", file->srv->cli, path,
01378                               &targetcli, targetpath))
01379         {
01380                 d_printf("Could not resolve %s\n", path);
01381                 return -1;
01382         }
01383         /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
01384 
01385 
01386         ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
01387 
01388         if (ret <= 0) {
01389 
01390                 errno = smbc_errno(context, targetcli);
01391                 return -1;
01392 
01393         }
01394 
01395         file->offset += ret;
01396 
01397         return ret;  /* Success, 0 bytes of data ... */
01398 }
01399  
01400 /*
01401  * Routine to close() a file ...
01402  */
01403 
01404 static int
01405 smbc_close_ctx(SMBCCTX *context,
01406                SMBCFILE *file)
01407 {
01408         SMBCSRV *srv; 
01409         fstring server, share, user, password;
01410         pstring path, targetpath;
01411         struct cli_state *targetcli;
01412 
01413         if (!context || !context->internal ||
01414             !context->internal->_initialized) {
01415 
01416                 errno = EINVAL;
01417                 return -1;
01418 
01419         }
01420 
01421         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
01422    
01423                 errno = EBADF;
01424                 return -1;
01425 
01426         }
01427 
01428         /* IS a dir ... */
01429         if (!file->file) {
01430                 
01431                 return (context->closedir)(context, file);
01432 
01433         }
01434 
01435         /*d_printf(">>>close: parsing %s\n", file->fname);*/
01436         if (smbc_parse_path(context, file->fname,
01437                             NULL, 0,
01438                             server, sizeof(server),
01439                             share, sizeof(share),
01440                             path, sizeof(path),
01441                             user, sizeof(user),
01442                             password, sizeof(password),
01443                             NULL, 0)) {
01444                 errno = EINVAL;
01445                 return -1;
01446         }
01447         
01448         /*d_printf(">>>close: resolving %s\n", path);*/
01449         if (!cli_resolve_path("", file->srv->cli, path,
01450                               &targetcli, targetpath))
01451         {
01452                 d_printf("Could not resolve %s\n", path);
01453                 return -1;
01454         }
01455         /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
01456 
01457         if (!cli_close(targetcli, file->cli_fd)) {
01458 
01459                 DEBUG(3, ("cli_close failed on %s. purging server.\n", 
01460                           file->fname));
01461                 /* Deallocate slot and remove the server 
01462                  * from the server cache if unused */
01463                 errno = smbc_errno(context, targetcli);
01464                 srv = file->srv;
01465                 DLIST_REMOVE(context->internal->_files, file);
01466                 SAFE_FREE(file->fname);
01467                 SAFE_FREE(file);
01468                 (context->callbacks.remove_unused_server_fn)(context, srv);
01469 
01470                 return -1;
01471 
01472         }
01473 
01474         DLIST_REMOVE(context->internal->_files, file);
01475         SAFE_FREE(file->fname);
01476         SAFE_FREE(file);
01477 
01478         return 0;
01479 }
01480 
01481 /*
01482  * Get info from an SMB server on a file. Use a qpathinfo call first
01483  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
01484  */
01485 static BOOL
01486 smbc_getatr(SMBCCTX * context,
01487             SMBCSRV *srv,
01488             char *path, 
01489             uint16 *mode,
01490             SMB_OFF_T *size, 
01491             struct timespec *create_time_ts,
01492             struct timespec *access_time_ts,
01493             struct timespec *write_time_ts,
01494             struct timespec *change_time_ts,
01495             SMB_INO_T *ino)
01496 {
01497         pstring fixedpath;
01498         pstring targetpath;
01499         struct cli_state *targetcli;
01500         time_t write_time;
01501 
01502         if (!context || !context->internal ||
01503             !context->internal->_initialized) {
01504  
01505                 errno = EINVAL;
01506                 return -1;
01507  
01508         }
01509 
01510         /* path fixup for . and .. */
01511         if (strequal(path, ".") || strequal(path, ".."))
01512                 pstrcpy(fixedpath, "\\");
01513         else
01514         {
01515                 pstrcpy(fixedpath, path);
01516                 trim_string(fixedpath, NULL, "\\..");
01517                 trim_string(fixedpath, NULL, "\\.");
01518         }
01519         DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
01520   
01521         if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
01522         {
01523                 d_printf("Couldn't resolve %s\n", path);
01524                 return False;
01525         }
01526         
01527         if (!srv->no_pathinfo2 &&
01528             cli_qpathinfo2(targetcli, targetpath,
01529                            create_time_ts,
01530                            access_time_ts,
01531                            write_time_ts,
01532                            change_time_ts,
01533                            size, mode, ino)) {
01534             return True;
01535         }
01536 
01537         /* if this is NT then don't bother with the getatr */
01538         if (targetcli->capabilities & CAP_NT_SMBS) {
01539                 errno = EPERM;
01540                 return False;
01541         }
01542 
01543         if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
01544 
01545                 struct timespec w_time_ts;
01546 
01547                 w_time_ts = convert_time_t_to_timespec(write_time);
01548 
01549                 if (write_time_ts != NULL) {
01550                         *write_time_ts = w_time_ts;
01551                 }
01552 
01553                 if (create_time_ts != NULL) {
01554                         *create_time_ts = w_time_ts;
01555                 }
01556                 
01557                 if (access_time_ts != NULL) {
01558                         *access_time_ts = w_time_ts;
01559                 }
01560                 
01561                 if (change_time_ts != NULL) {
01562                         *change_time_ts = w_time_ts;
01563                 }
01564 
01565                 srv->no_pathinfo2 = True;
01566                 return True;
01567         }
01568 
01569         errno = EPERM;
01570         return False;
01571 
01572 }
01573 
01574 /*
01575  * Set file info on an SMB server.  Use setpathinfo call first.  If that
01576  * fails, use setattrE..
01577  *
01578  * Access and modification time parameters are always used and must be
01579  * provided.  Create time, if zero, will be determined from the actual create
01580  * time of the file.  If non-zero, the create time will be set as well.
01581  *
01582  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
01583  */
01584 static BOOL
01585 smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
01586             time_t create_time,
01587             time_t access_time,
01588             time_t write_time,
01589             time_t change_time,
01590             uint16 mode)
01591 {
01592         int fd;
01593         int ret;
01594 
01595         /*
01596          * First, try setpathinfo (if qpathinfo succeeded), for it is the
01597          * modern function for "new code" to be using, and it works given a
01598          * filename rather than requiring that the file be opened to have its
01599          * attributes manipulated.
01600          */
01601         if (srv->no_pathinfo ||
01602             ! cli_setpathinfo(srv->cli, path,
01603                               create_time,
01604                               access_time,
01605                               write_time,
01606                               change_time,
01607                               mode)) {
01608 
01609                 /*
01610                  * setpathinfo is not supported; go to plan B. 
01611                  *
01612                  * cli_setatr() does not work on win98, and it also doesn't
01613                  * support setting the access time (only the modification
01614                  * time), so in all cases, we open the specified file and use
01615                  * cli_setattrE() which should work on all OS versions, and
01616                  * supports both times.
01617                  */
01618 
01619                 /* Don't try {q,set}pathinfo() again, with this server */
01620                 srv->no_pathinfo = True;
01621 
01622                 /* Open the file */
01623                 if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
01624 
01625                         errno = smbc_errno(context, srv->cli);
01626                         return -1;
01627                 }
01628 
01629                 /* Set the new attributes */
01630                 ret = cli_setattrE(srv->cli, fd,
01631                                    change_time,
01632                                    access_time,
01633                                    write_time);
01634 
01635                 /* Close the file */
01636                 cli_close(srv->cli, fd);
01637 
01638                 /*
01639                  * Unfortunately, setattrE() doesn't have a provision for
01640                  * setting the access mode (attributes).  We'll have to try
01641                  * cli_setatr() for that, and with only this parameter, it
01642                  * seems to work on win98.
01643                  */
01644                 if (ret && mode != (uint16) -1) {
01645                         ret = cli_setatr(srv->cli, path, mode, 0);
01646                 }
01647 
01648                 if (! ret) {
01649                         errno = smbc_errno(context, srv->cli);
01650                         return False;
01651                 }
01652         }
01653 
01654         return True;
01655 }
01656 
01657  /*
01658   * Routine to unlink() a file
01659   */
01660 
01661 static int
01662 smbc_unlink_ctx(SMBCCTX *context,
01663                 const char *fname)
01664 {
01665         fstring server, share, user, password, workgroup;
01666         pstring path, targetpath;
01667         struct cli_state *targetcli;
01668         SMBCSRV *srv = NULL;
01669 
01670         if (!context || !context->internal ||
01671             !context->internal->_initialized) {
01672 
01673                 errno = EINVAL;  /* Best I can think of ... */
01674                 return -1;
01675 
01676         }
01677 
01678         if (!fname) {
01679 
01680                 errno = EINVAL;
01681                 return -1;
01682 
01683         }
01684 
01685         if (smbc_parse_path(context, fname,
01686                             workgroup, sizeof(workgroup),
01687                             server, sizeof(server),
01688                             share, sizeof(share),
01689                             path, sizeof(path),
01690                             user, sizeof(user),
01691                             password, sizeof(password),
01692                             NULL, 0)) {
01693                 errno = EINVAL;
01694                 return -1;
01695         }
01696 
01697         if (user[0] == (char)0) fstrcpy(user, context->user);
01698 
01699         srv = smbc_server(context, True,
01700                           server, share, workgroup, user, password);
01701 
01702         if (!srv) {
01703 
01704                 return -1;  /* smbc_server sets errno */
01705 
01706         }
01707 
01708         /*d_printf(">>>unlink: resolving %s\n", path);*/
01709         if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
01710         {
01711                 d_printf("Could not resolve %s\n", path);
01712                 return -1;
01713         }
01714         /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
01715 
01716         if (!cli_unlink(targetcli, targetpath)) {
01717 
01718                 errno = smbc_errno(context, targetcli);
01719 
01720                 if (errno == EACCES) { /* Check if the file is a directory */
01721 
01722                         int saverr = errno;
01723                         SMB_OFF_T size = 0;
01724                         uint16 mode = 0;
01725                         struct timespec write_time_ts;
01726                         struct timespec access_time_ts;
01727                         struct timespec change_time_ts;
01728                         SMB_INO_T ino = 0;
01729 
01730                         if (!smbc_getatr(context, srv, path, &mode, &size,
01731                                          NULL,
01732                                          &access_time_ts,
01733                                          &write_time_ts,
01734                                          &change_time_ts,
01735                                          &ino)) {
01736 
01737                                 /* Hmmm, bad error ... What? */
01738 
01739                                 errno = smbc_errno(context, targetcli);
01740                                 return -1;
01741 
01742                         }
01743                         else {
01744 
01745                                 if (IS_DOS_DIR(mode))
01746                                         errno = EISDIR;
01747                                 else
01748                                         errno = saverr;  /* Restore this */
01749 
01750                         }
01751                 }
01752 
01753                 return -1;
01754 
01755         }
01756 
01757         return 0;  /* Success ... */
01758 
01759 }
01760 
01761 /*
01762  * Routine to rename() a file
01763  */
01764 
01765 static int
01766 smbc_rename_ctx(SMBCCTX *ocontext,
01767                 const char *oname, 
01768                 SMBCCTX *ncontext,
01769                 const char *nname)
01770 {
01771         fstring server1;
01772         fstring share1;
01773         fstring server2;
01774         fstring share2;
01775         fstring user1;
01776         fstring user2;
01777         fstring password1;
01778         fstring password2;
01779         fstring workgroup;
01780         pstring path1;
01781         pstring path2;
01782         pstring targetpath1;
01783         pstring targetpath2;
01784         struct cli_state *targetcli1;
01785         struct cli_state *targetcli2;
01786         SMBCSRV *srv = NULL;
01787 
01788         if (!ocontext || !ncontext || 
01789             !ocontext->internal || !ncontext->internal ||
01790             !ocontext->internal->_initialized || 
01791             !ncontext->internal->_initialized) {
01792 
01793                 errno = EINVAL;  /* Best I can think of ... */
01794                 return -1;
01795 
01796         }
01797         
01798         if (!oname || !nname) {
01799 
01800                 errno = EINVAL;
01801                 return -1;
01802 
01803         }
01804         
01805         DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
01806 
01807         smbc_parse_path(ocontext, oname,
01808                         workgroup, sizeof(workgroup),
01809                         server1, sizeof(server1),
01810                         share1, sizeof(share1),
01811                         path1, sizeof(path1),
01812                         user1, sizeof(user1),
01813                         password1, sizeof(password1),
01814                         NULL, 0);
01815 
01816         if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
01817 
01818         smbc_parse_path(ncontext, nname,
01819                         NULL, 0,
01820                         server2, sizeof(server2),
01821                         share2, sizeof(share2),
01822                         path2, sizeof(path2),
01823                         user2, sizeof(user2),
01824                         password2, sizeof(password2),
01825                         NULL, 0);
01826 
01827         if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
01828 
01829         if (strcmp(server1, server2) || strcmp(share1, share2) ||
01830             strcmp(user1, user2)) {
01831 
01832                 /* Can't rename across file systems, or users?? */
01833 
01834                 errno = EXDEV;
01835                 return -1;
01836 
01837         }
01838 
01839         srv = smbc_server(ocontext, True,
01840                           server1, share1, workgroup, user1, password1);
01841         if (!srv) {
01842 
01843                 return -1;
01844 
01845         }
01846 
01847         /*d_printf(">>>rename: resolving %s\n", path1);*/
01848         if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
01849         {
01850                 d_printf("Could not resolve %s\n", path1);
01851                 return -1;
01852         }
01853         /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
01854         /*d_printf(">>>rename: resolving %s\n", path2);*/
01855         if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
01856         {
01857                 d_printf("Could not resolve %s\n", path2);
01858                 return -1;
01859         }
01860         /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
01861         
01862         if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
01863             strcmp(targetcli1->share, targetcli2->share))
01864         {
01865                 /* can't rename across file systems */
01866                 
01867                 errno = EXDEV;
01868                 return -1;
01869         }
01870 
01871         if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
01872                 int eno = smbc_errno(ocontext, targetcli1);
01873 
01874                 if (eno != EEXIST ||
01875                     !cli_unlink(targetcli1, targetpath2) ||
01876                     !cli_rename(targetcli1, targetpath1, targetpath2)) {
01877 
01878                         errno = eno;
01879                         return -1;
01880 
01881                 }
01882         }
01883 
01884         return 0; /* Success */
01885 
01886 }
01887 
01888 /*
01889  * A routine to lseek() a file
01890  */
01891 
01892 static off_t
01893 smbc_lseek_ctx(SMBCCTX *context,
01894                SMBCFILE *file,
01895                off_t offset,
01896                int whence)
01897 {
01898         SMB_OFF_T size;
01899         fstring server, share, user, password;
01900         pstring path, targetpath;
01901         struct cli_state *targetcli;
01902 
01903         if (!context || !context->internal ||
01904             !context->internal->_initialized) {
01905 
01906                 errno = EINVAL;
01907                 return -1;
01908                 
01909         }
01910 
01911         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
01912 
01913                 errno = EBADF;
01914                 return -1;
01915 
01916         }
01917 
01918         if (!file->file) {
01919 
01920                 errno = EINVAL;
01921                 return -1;      /* Can't lseek a dir ... */
01922 
01923         }
01924 
01925         switch (whence) {
01926         case SEEK_SET:
01927                 file->offset = offset;
01928                 break;
01929 
01930         case SEEK_CUR:
01931                 file->offset += offset;
01932                 break;
01933 
01934         case SEEK_END:
01935                 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
01936                 if (smbc_parse_path(context, file->fname,
01937                                     NULL, 0,
01938                                     server, sizeof(server),
01939                                     share, sizeof(share),
01940                                     path, sizeof(path),
01941                                     user, sizeof(user),
01942                                     password, sizeof(password),
01943                                     NULL, 0)) {
01944                         
01945                                         errno = EINVAL;
01946                                         return -1;
01947                         }
01948                 
01949                 /*d_printf(">>>lseek: resolving %s\n", path);*/
01950                 if (!cli_resolve_path("", file->srv->cli, path,
01951                                       &targetcli, targetpath))
01952                 {
01953                         d_printf("Could not resolve %s\n", path);
01954                         return -1;
01955                 }
01956                 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
01957                 
01958                 if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
01959                                    &size, NULL, NULL, NULL, NULL, NULL)) 
01960                 {
01961                     SMB_OFF_T b_size = size;
01962                         if (!cli_getattrE(targetcli, file->cli_fd,
01963                                           NULL, &b_size, NULL, NULL, NULL)) 
01964                     {
01965                         errno = EINVAL;
01966                         return -1;
01967                     } else
01968                         size = b_size;
01969                 }
01970                 file->offset = size + offset;
01971                 break;
01972 
01973         default:
01974                 errno = EINVAL;
01975                 break;
01976 
01977         }
01978 
01979         return file->offset;
01980 
01981 }
01982 
01983 /* 
01984  * Generate an inode number from file name for those things that need it
01985  */
01986 
01987 static ino_t
01988 smbc_inode(SMBCCTX *context,
01989            const char *name)
01990 {
01991 
01992         if (!context || !context->internal ||
01993             !context->internal->_initialized) {
01994 
01995                 errno = EINVAL;
01996                 return -1;
01997 
01998         }
01999 
02000         if (!*name) return 2; /* FIXME, why 2 ??? */
02001         return (ino_t)str_checksum(name);
02002 
02003 }
02004 
02005 /*
02006  * Routine to put basic stat info into a stat structure ... Used by stat and
02007  * fstat below.
02008  */
02009 
02010 static int
02011 smbc_setup_stat(SMBCCTX *context,
02012                 struct stat *st,
02013                 char *fname,
02014                 SMB_OFF_T size,
02015                 int mode)
02016 {
02017         
02018         st->st_mode = 0;
02019 
02020         if (IS_DOS_DIR(mode)) {
02021                 st->st_mode = SMBC_DIR_MODE;
02022         } else {
02023                 st->st_mode = SMBC_FILE_MODE;
02024         }
02025 
02026         if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
02027         if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
02028         if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
02029         if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
02030 
02031         st->st_size = size;
02032 #ifdef HAVE_STAT_ST_BLKSIZE
02033         st->st_blksize = 512;
02034 #endif
02035 #ifdef HAVE_STAT_ST_BLOCKS
02036         st->st_blocks = (size+511)/512;
02037 #endif
02038         st->st_uid = getuid();
02039         st->st_gid = getgid();
02040 
02041         if (IS_DOS_DIR(mode)) {
02042                 st->st_nlink = 2;
02043         } else {
02044                 st->st_nlink = 1;
02045         }
02046 
02047         if (st->st_ino == 0) {
02048                 st->st_ino = smbc_inode(context, fname);
02049         }
02050         
02051         return True;  /* FIXME: Is this needed ? */
02052 
02053 }
02054 
02055 /*
02056  * Routine to stat a file given a name
02057  */
02058 
02059 static int
02060 smbc_stat_ctx(SMBCCTX *context,
02061               const char *fname,
02062               struct stat *st)
02063 {
02064         SMBCSRV *srv;
02065         fstring server;
02066         fstring share;
02067         fstring user;
02068         fstring password;
02069         fstring workgroup;
02070         pstring path;
02071         struct timespec write_time_ts;
02072         struct timespec access_time_ts;
02073         struct timespec change_time_ts;
02074         SMB_OFF_T size = 0;
02075         uint16 mode = 0;
02076         SMB_INO_T ino = 0;
02077 
02078         if (!context || !context->internal ||
02079             !context->internal->_initialized) {
02080 
02081                 errno = EINVAL;  /* Best I can think of ... */
02082                 return -1;
02083     
02084         }
02085 
02086         if (!fname) {
02087 
02088                 errno = EINVAL;
02089                 return -1;
02090 
02091         }
02092   
02093         DEBUG(4, ("smbc_stat(%s)\n", fname));
02094 
02095         if (smbc_parse_path(context, fname,
02096                             workgroup, sizeof(workgroup),
02097                             server, sizeof(server),
02098                             share, sizeof(share),
02099                             path, sizeof(path),
02100                             user, sizeof(user),
02101                             password, sizeof(password),
02102                             NULL, 0)) {
02103                 errno = EINVAL;
02104                 return -1;
02105         }
02106 
02107         if (user[0] == (char)0) fstrcpy(user, context->user);
02108 
02109         srv = smbc_server(context, True,
02110                           server, share, workgroup, user, password);
02111 
02112         if (!srv) {
02113                 return -1;  /* errno set by smbc_server */
02114         }
02115 
02116         if (!smbc_getatr(context, srv, path, &mode, &size, 
02117                          NULL,
02118                          &access_time_ts,
02119                          &write_time_ts,
02120                          &change_time_ts,
02121                          &ino)) {
02122 
02123                 errno = smbc_errno(context, srv->cli);
02124                 return -1;
02125                 
02126         }
02127 
02128         st->st_ino = ino;
02129 
02130         smbc_setup_stat(context, st, path, size, mode);
02131 
02132         set_atimespec(st, access_time_ts);
02133         set_ctimespec(st, change_time_ts);
02134         set_mtimespec(st, write_time_ts);
02135         st->st_dev   = srv->dev;
02136 
02137         return 0;
02138 
02139 }
02140 
02141 /*
02142  * Routine to stat a file given an fd
02143  */
02144 
02145 static int
02146 smbc_fstat_ctx(SMBCCTX *context,
02147                SMBCFILE *file,
02148                struct stat *st)
02149 {
02150         struct timespec change_time_ts;
02151         struct timespec access_time_ts;
02152         struct timespec write_time_ts;
02153         SMB_OFF_T size;
02154         uint16 mode;
02155         fstring server;
02156         fstring share;
02157         fstring user;
02158         fstring password;
02159         pstring path;
02160         pstring targetpath;
02161         struct cli_state *targetcli;
02162         SMB_INO_T ino = 0;
02163 
02164         if (!context || !context->internal ||
02165             !context->internal->_initialized) {
02166 
02167                 errno = EINVAL;
02168                 return -1;
02169 
02170         }
02171 
02172         if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
02173 
02174                 errno = EBADF;
02175                 return -1;
02176 
02177         }
02178 
02179         if (!file->file) {
02180 
02181                 return (context->fstatdir)(context, file, st);
02182 
02183         }
02184 
02185         /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
02186         if (smbc_parse_path(context, file->fname,
02187                             NULL, 0,
02188                             server, sizeof(server),
02189                             share, sizeof(share),
02190                             path, sizeof(path),
02191                             user, sizeof(user),
02192                             password, sizeof(password),
02193                             NULL, 0)) {
02194                 errno = EINVAL;
02195                 return -1;
02196         }
02197         
02198         /*d_printf(">>>fstat: resolving %s\n", path);*/
02199         if (!cli_resolve_path("", file->srv->cli, path,
02200                               &targetcli, targetpath))
02201         {
02202                 d_printf("Could not resolve %s\n", path);
02203                 return -1;
02204         }
02205         /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
02206 
02207         if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
02208                            NULL,
02209                            &access_time_ts,
02210                            &write_time_ts,
02211                            &change_time_ts,
02212                            &ino)) {
02213 
02214                 time_t change_time, access_time, write_time;
02215 
02216                 if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
02217                                 &change_time, &access_time, &write_time)) {
02218 
02219                         errno = EINVAL;
02220                         return -1;
02221                 }
02222 
02223                 change_time_ts = convert_time_t_to_timespec(change_time);
02224                 access_time_ts = convert_time_t_to_timespec(access_time);
02225                 write_time_ts = convert_time_t_to_timespec(write_time);
02226         }
02227 
02228         st->st_ino = ino;
02229 
02230         smbc_setup_stat(context, st, file->fname, size, mode);
02231 
02232         set_atimespec(st, access_time_ts);
02233         set_ctimespec(st, change_time_ts);
02234         set_mtimespec(st, write_time_ts);
02235         st->st_dev = file->srv->dev;
02236 
02237         return 0;
02238 
02239 }
02240 
02241 /*
02242  * Routine to open a directory
02243  * We accept the URL syntax explained in smbc_parse_path(), above.
02244  */
02245 
02246 static void
02247 smbc_remove_dir(SMBCFILE *dir)
02248 {
02249         struct smbc_dir_list *d,*f;
02250 
02251         d = dir->dir_list;
02252         while (d) {
02253 
02254                 f = d; d = d->next;
02255 
02256                 SAFE_FREE(f->dirent);
02257                 SAFE_FREE(f);
02258 
02259         }
02260 
02261         dir->dir_list = dir->dir_end = dir->dir_next = NULL;
02262 
02263 }
02264 
02265 static int
02266 add_dirent(SMBCFILE *dir,
02267            const char *name,
02268            const char *comment,
02269            uint32 type)
02270 {
02271         struct smbc_dirent *dirent;
02272         int size;
02273         int name_length = (name == NULL ? 0 : strlen(name));
02274         int comment_len = (comment == NULL ? 0 : strlen(comment));
02275 
02276         /*
02277          * Allocate space for the dirent, which must be increased by the 
02278          * size of the name and the comment and 1 each for the null terminator.
02279          */
02280 
02281         size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
02282     
02283         dirent = (struct smbc_dirent *)SMB_MALLOC(size);
02284 
02285         if (!dirent) {
02286 
02287                 dir->dir_error = ENOMEM;
02288                 return -1;
02289 
02290         }
02291 
02292         ZERO_STRUCTP(dirent);
02293 
02294         if (dir->dir_list == NULL) {
02295 
02296                 dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
02297                 if (!dir->dir_list) {
02298 
02299                         SAFE_FREE(dirent);
02300                         dir->dir_error = ENOMEM;
02301                         return -1;
02302 
02303                 }
02304                 ZERO_STRUCTP(dir->dir_list);
02305 
02306                 dir->dir_end = dir->dir_next = dir->dir_list;
02307         }
02308         else {
02309 
02310                 dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
02311                 
02312                 if (!dir->dir_end->next) {
02313                         
02314                         SAFE_FREE(dirent);
02315                         dir->dir_error = ENOMEM;
02316                         return -1;
02317 
02318                 }
02319                 ZERO_STRUCTP(dir->dir_end->next);
02320 
02321                 dir->dir_end = dir->dir_end->next;
02322         }
02323 
02324         dir->dir_end->next = NULL;
02325         dir->dir_end->dirent = dirent;
02326         
02327         dirent->smbc_type = type;
02328         dirent->namelen = name_length;
02329         dirent->commentlen = comment_len;
02330         dirent->dirlen = size;
02331   
02332         /*
02333          * dirent->namelen + 1 includes the null (no null termination needed)
02334          * Ditto for dirent->commentlen.
02335          * The space for the two null bytes was allocated.
02336          */
02337         strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
02338         dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
02339         strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
02340         
02341         return 0;
02342 
02343 }
02344 
02345 static void
02346 list_unique_wg_fn(const char *name,
02347                   uint32 type,
02348                   const char *comment,
02349                   void *state)
02350 {
02351         SMBCFILE *dir = (SMBCFILE *)state;
02352         struct smbc_dir_list *dir_list;
02353         struct smbc_dirent *dirent;
02354         int dirent_type;
02355         int do_remove = 0;
02356 
02357         dirent_type = dir->dir_type;
02358 
02359         if (add_dirent(dir, name, comment, dirent_type) < 0) {
02360 
02361                 /* An error occurred, what do we do? */
02362                 /* FIXME: Add some code here */
02363         }
02364 
02365         /* Point to the one just added */
02366         dirent = dir->dir_end->dirent;
02367 
02368         /* See if this was a duplicate */
02369         for (dir_list = dir->dir_list;
02370              dir_list != dir->dir_end;
02371              dir_list = dir_list->next) {
02372                 if (! do_remove &&
02373                     strcmp(dir_list->dirent->name, dirent->name) == 0) {
02374                         /* Duplicate.  End end of list need to be removed. */
02375                         do_remove = 1;
02376                 }
02377 
02378                 if (do_remove && dir_list->next == dir->dir_end) {
02379                         /* Found the end of the list.  Remove it. */
02380                         dir->dir_end = dir_list;
02381                         free(dir_list->next);
02382                         free(dirent);
02383                         dir_list->next = NULL;
02384                         break;
02385                 }
02386         }
02387 }
02388 
02389 static void
02390 list_fn(const char *name,
02391         uint32 type,
02392         const char *comment,
02393         void *state)
02394 {
02395         SMBCFILE *dir = (SMBCFILE *)state;
02396         int dirent_type;
02397 
02398         /*
02399          * We need to process the type a little ...
02400          *
02401          * Disk share     = 0x00000000
02402          * Print share    = 0x00000001
02403          * Comms share    = 0x00000002 (obsolete?)
02404          * IPC$ share     = 0x00000003 
02405          *
02406          * administrative shares:
02407          * ADMIN$, IPC$, C$, D$, E$ ...  are type |= 0x80000000
02408          */
02409         
02410         if (dir->dir_type == SMBC_FILE_SHARE) {
02411                 
02412                 switch (type) {
02413                 case 0 | 0x80000000:
02414                 case 0:
02415                         dirent_type = SMBC_FILE_SHARE;
02416                         break;
02417 
02418                 case 1:
02419                         dirent_type = SMBC_PRINTER_SHARE;
02420                         break;
02421 
02422                 case 2:
02423                         dirent_type = SMBC_COMMS_SHARE;
02424                         break;
02425 
02426                 case 3 | 0x80000000:
02427                 case 3:
02428                         dirent_type = SMBC_IPC_SHARE;
02429                         break;
02430 
02431                 default:
02432                         dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
02433                         break;
02434                 }
02435         }
02436         else {
02437                 dirent_type = dir->dir_type;
02438         }
02439 
02440         if (add_dirent(dir, name, comment, dirent_type) < 0) {
02441 
02442                 /* An error occurred, what do we do? */
02443                 /* FIXME: Add some code here */
02444 
02445         }
02446 }
02447 
02448 static void
02449 dir_list_fn(const char *mnt,
02450             file_info *finfo,
02451             const char *mask,
02452             void *state)
02453 {
02454 
02455         if (add_dirent((SMBCFILE *)state, finfo->name, "", 
02456                        (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
02457 
02458                 /* Handle an error ... */
02459 
02460                 /* FIXME: Add some code ... */
02461 
02462         } 
02463 
02464 }
02465 
02466 static int
02467 net_share_enum_rpc(struct cli_state *cli,
02468                    void (*fn)(const char *name,
02469                               uint32 type,
02470                               const char *comment,
02471                               void *state),
02472                    void *state)
02473 {
02474         int i;
02475         WERROR result;
02476         ENUM_HND enum_hnd;
02477         uint32 info_level = 1;
02478         uint32 preferred_len = 0xffffffff;
02479         uint32 type;
02480         SRV_SHARE_INFO_CTR ctr;
02481         fstring name = "";
02482         fstring comment = "";
02483         void *mem_ctx;
02484         struct rpc_pipe_client *pipe_hnd;
02485         NTSTATUS nt_status;
02486 
02487         /* Open the server service pipe */
02488         pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
02489         if (!pipe_hnd) {
02490                 DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
02491                 return -1;
02492         }
02493 
02494         /* Allocate a context for parsing and for the entries in "ctr" */
02495         mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
02496         if (mem_ctx == NULL) {
02497                 DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
02498                 cli_rpc_pipe_close(pipe_hnd);
02499                 return -1; 
02500         }
02501 
02502         /* Issue the NetShareEnum RPC call and retrieve the response */
02503         init_enum_hnd(&enum_hnd, 0);
02504         result = rpccli_srvsvc_net_share_enum(pipe_hnd,
02505                                               mem_ctx,
02506                                               info_level,
02507                                               &ctr,
02508                                               preferred_len,
02509                                               &enum_hnd);
02510 
02511         /* Was it successful? */
02512         if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
02513                 /*  Nope.  Go clean up. */
02514                 goto done;
02515         }
02516 
02517         /* For each returned entry... */
02518         for (i = 0; i < ctr.num_entries; i++) {
02519 
02520                 /* pull out the share name */
02521                 rpcstr_pull_unistr2_fstring(
02522                         name, &ctr.share.info1[i].info_1_str.uni_netname);
02523 
02524                 /* pull out the share's comment */
02525                 rpcstr_pull_unistr2_fstring(
02526                         comment, &ctr.share.info1[i].info_1_str.uni_remark);
02527 
02528                 /* Get the type value */
02529                 type = ctr.share.info1[i].info_1.type;
02530 
02531                 /* Add this share to the list */
02532                 (*fn)(name, type, comment, state);
02533         }
02534 
02535 done:
02536         /* Close the server service pipe */
02537         cli_rpc_pipe_close(pipe_hnd);
02538 
02539         /* Free all memory which was allocated for this request */
02540         TALLOC_FREE(mem_ctx);
02541 
02542         /* Tell 'em if it worked */
02543         return W_ERROR_IS_OK(result) ? 0 : -1;
02544 }
02545 
02546 
02547 
02548 static SMBCFILE *
02549 smbc_opendir_ctx(SMBCCTX *context,
02550                  const char *fname)
02551 {
02552         int saved_errno;
02553         fstring server, share, user, password, options;
02554         pstring workgroup;
02555         pstring path;
02556         uint16 mode;
02557         char *p;
02558         SMBCSRV *srv  = NULL;
02559         SMBCFILE *dir = NULL;
02560         struct _smbc_callbacks *cb;
02561         struct in_addr rem_ip;
02562 
02563         if (!context || !context->internal ||
02564             !context->internal->_initialized) {
02565                 DEBUG(4, ("no valid context\n"));
02566                 errno = EINVAL + 8192;
02567                 return NULL;
02568 
02569         }
02570 
02571         if (!fname) {
02572                 DEBUG(4, ("no valid fname\n"));
02573                 errno = EINVAL + 8193;
02574                 return NULL;
02575         }
02576 
02577         if (smbc_parse_path(context, fname,
02578                             workgroup, sizeof(workgroup),
02579                             server, sizeof(server),
02580                             share, sizeof(share),
02581                             path, sizeof(path),
02582                             user, sizeof(user),
02583                             password, sizeof(password),
02584                             options, sizeof(options))) {
02585                 DEBUG(4, ("no valid path\n"));
02586                 errno = EINVAL + 8194;
02587                 return NULL;
02588         }
02589 
02590         DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
02591                   "path='%s' options='%s'\n",
02592                   fname, server, share, path, options));
02593 
02594         /* Ensure the options are valid */
02595         if (smbc_check_options(server, share, path, options)) {
02596                 DEBUG(4, ("unacceptable options (%s)\n", options));
02597                 errno = EINVAL + 8195;
02598                 return NULL;
02599         }
02600 
02601         if (user[0] == (char)0) fstrcpy(user, context->user);
02602 
02603         dir = SMB_MALLOC_P(SMBCFILE);
02604 
02605         if (!dir) {
02606 
02607                 errno = ENOMEM;
02608                 return NULL;
02609 
02610         }
02611 
02612         ZERO_STRUCTP(dir);
02613 
02614         dir->cli_fd   = 0;
02615         dir->fname    = SMB_STRDUP(fname);
02616         dir->srv      = NULL;
02617         dir->offset   = 0;
02618         dir->file     = False;
02619         dir->dir_list = dir->dir_next = dir->dir_end = NULL;
02620 
02621         if (server[0] == (char)0) {
02622 
02623                 int i;
02624                 int count;
02625                 int max_lmb_count;
02626                 struct ip_service *ip_list;
02627                 struct ip_service server_addr;
02628                 struct user_auth_info u_info;
02629                 struct cli_state *cli;
02630 
02631                 if (share[0] != (char)0 || path[0] != (char)0) {
02632 
02633                         errno = EINVAL + 8196;
02634                         if (dir) {
02635                                 SAFE_FREE(dir->fname);
02636                                 SAFE_FREE(dir);
02637                         }
02638                         return NULL;
02639                 }
02640 
02641                 /* Determine how many local master browsers to query */
02642                 max_lmb_count = (context->options.browse_max_lmb_count == 0
02643                                  ? INT_MAX
02644                                  : context->options.browse_max_lmb_count);
02645 
02646                 pstrcpy(u_info.username, user);
02647                 pstrcpy(u_info.password, password);
02648 
02649                 /*
02650                  * We have server and share and path empty but options
02651                  * requesting that we scan all master browsers for their list
02652                  * of workgroups/domains.  This implies that we must first try
02653                  * broadcast queries to find all master browsers, and if that
02654                  * doesn't work, then try our other methods which return only
02655                  * a single master browser.
02656                  */
02657 
02658                 ip_list = NULL;
02659                 if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
02660 
02661                         SAFE_FREE(ip_list);
02662 
02663                         if (!find_master_ip(workgroup, &server_addr.ip)) {
02664 
02665                                 if (dir) {
02666                                         SAFE_FREE(dir->fname);
02667                                         SAFE_FREE(dir);
02668                                 }
02669                                 errno = ENOENT;
02670                                 return NULL;
02671                         }
02672 
02673                         ip_list = &server_addr;
02674                         count = 1;
02675                 }
02676 
02677                 for (i = 0; i < count && i < max_lmb_count; i++) {
02678                         DEBUG(99, ("Found master browser %d of %d: %s\n",
02679                                    i+1, MAX(count, max_lmb_count),
02680                                    inet_ntoa(ip_list[i].ip)));
02681                         
02682                         cli = get_ipc_connect_master_ip(&ip_list[i],
02683                                                         workgroup, &u_info);
02684                         /* cli == NULL is the master browser refused to talk or 
02685                            could not be found */
02686                         if ( !cli )
02687                                 continue;
02688 
02689                         fstrcpy(server, cli->desthost);
02690                         cli_shutdown(cli);
02691 
02692                         DEBUG(4, ("using workgroup %s %s\n",
02693                                   workgroup, server));
02694 
02695                         /*
02696                          * For each returned master browser IP address, get a
02697                          * connection to IPC$ on the server if we do not
02698                          * already have one, and determine the
02699                          * workgroups/domains that it knows about.
02700                          */
02701                 
02702                         srv = smbc_server(context, True, server, "IPC$",
02703                                           workgroup, user, password);
02704                         if (!srv) {
02705                                 continue;
02706                         }
02707                 
02708                         dir->srv = srv;
02709                         dir->dir_type = SMBC_WORKGROUP;
02710 
02711                         /* Now, list the stuff ... */
02712                         
02713                         if (!cli_NetServerEnum(srv->cli,
02714                                                workgroup,
02715                                                SV_TYPE_DOMAIN_ENUM,
02716                                                list_unique_wg_fn,
02717                                                (void *)dir)) {
02718                                 continue;
02719                         }
02720                 }
02721 
02722                 SAFE_FREE(ip_list);
02723         } else { 
02724                 /*
02725                  * Server not an empty string ... Check the rest and see what
02726                  * gives
02727                  */
02728                 if (*share == '\0') {
02729                         if (*path != '\0') {
02730 
02731                                 /* Should not have empty share with path */
02732                                 errno = EINVAL + 8197;
02733                                 if (dir) {
02734                                         SAFE_FREE(dir->fname);
02735                                         SAFE_FREE(dir);
02736                                 }
02737                                 return NULL;
02738         
02739                         }
02740 
02741                         /*
02742                          * We don't know if <server> is really a server name
02743                          * or is a workgroup/domain name.  If we already have
02744                          * a server structure for it, we'll use it.
02745                          * Otherwise, check to see if <server><1D>,
02746                          * <server><1B>, or <server><20> translates.  We check
02747                          * to see if <server> is an IP address first.
02748                          */
02749 
02750                         /*
02751                          * See if we have an existing server.  Do not
02752                          * establish a connection if one does not already
02753                          * exist.
02754                          */
02755                         srv = smbc_server(context, False, server, "IPC$",
02756                                           workgroup, user, password);
02757 
02758                         /*
02759                          * If no existing server and not an IP addr, look for
02760                          * LMB or DMB
02761                          */
02762                         if (!srv &&
02763                             !is_ipaddress(server) &&
02764                             (resolve_name(server, &rem_ip, 0x1d) ||   /* LMB */
02765                              resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
02766 
02767                                 fstring buserver;
02768 
02769                                 dir->dir_type = SMBC_SERVER;
02770 
02771                                 /*
02772                                  * Get the backup list ...
02773                                  */
02774                                 if (!name_status_find(server, 0, 0,
02775                                                       rem_ip, buserver)) {
02776 
02777                                         DEBUG(0, ("Could not get name of "
02778                                                   "local/domain master browser "
02779                                                   "for server %s\n", server));
02780                                         if (dir) {
02781                                                 SAFE_FREE(dir->fname);
02782                                                 SAFE_FREE(dir);
02783                                         }
02784                                         errno = EPERM;
02785                                         return NULL;
02786 
02787                                 }
02788 
02789                                 /*
02790                                  * Get a connection to IPC$ on the server if
02791                                  * we do not already have one
02792                                  */
02793                                 srv = smbc_server(context, True,
02794                                                   buserver, "IPC$",
02795                                                   workgroup, user, password);
02796                                 if (!srv) {
02797                                         DEBUG(0, ("got no contact to IPC$\n"));
02798                                         if (dir) {
02799                                                 SAFE_FREE(dir->fname);
02800                                                 SAFE_FREE(dir);
02801                                         }
02802                                         return NULL;
02803 
02804                                 }
02805 
02806                                 dir->srv = srv;
02807 
02808                                 /* Now, list the servers ... */
02809                                 if (!cli_NetServerEnum(srv->cli, server,
02810                                                        0x0000FFFE, list_fn,
02811                                                        (void *)dir)) {
02812 
02813                                         if (dir) {
02814                                                 SAFE_FREE(dir->fname);
02815                                                 SAFE_FREE(dir);
02816                                         }
02817                                         return NULL;
02818                                 }
02819                         } else if (srv ||
02820                                    (resolve_name(server, &rem_ip, 0x20))) {
02821                                 
02822                                 /* If we hadn't found the server, get one now */
02823                                 if (!srv) {
02824                                         srv = smbc_server(context, True,
02825                                                           server, "IPC$",
02826                                                           workgroup,
02827                                                           user, password);
02828                                 }
02829 
02830                                 if (!srv) {
02831                                         if (dir) {
02832                                                 SAFE_FREE(dir->fname);
02833                                                 SAFE_FREE(dir);
02834                                         }
02835                                         return NULL;
02836 
02837                                 }
02838 
02839                                 dir->dir_type = SMBC_FILE_SHARE;
02840                                 dir->srv = srv;
02841 
02842                                 /* List the shares ... */
02843 
02844                                 if (net_share_enum_rpc(
02845                                             srv->cli,
02846                                             list_fn,
02847                                             (void *) dir) < 0 &&
02848                                     cli_RNetShareEnum(
02849                                             srv->cli,
02850                                             list_fn, 
02851                                             (void *)dir) < 0) {
02852                                                 
02853                                         errno = cli_errno(srv->cli);
02854                                         if (dir) {
02855                                                 SAFE_FREE(dir->fname);
02856                                                 SAFE_FREE(dir);
02857                                         }
02858                                         return NULL;
02859 
02860                                 }
02861                         } else {
02862                                 /* Neither the workgroup nor server exists */
02863                                 errno = ECONNREFUSED;   
02864                                 if (dir) {
02865                                         SAFE_FREE(dir->fname);
02866                                         SAFE_FREE(dir);
02867                                 }
02868                                 return NULL;
02869                         }
02870 
02871                 }
02872                 else {
02873                         /*
02874                          * The server and share are specified ... work from
02875                          * there ...
02876                          */
02877                         pstring targetpath;
02878                         struct cli_state *targetcli;
02879 
02880                         /* We connect to the server and list the directory */
02881                         dir->dir_type = SMBC_FILE_SHARE;
02882 
02883                         srv = smbc_server(context, True, server, share,
02884                                           workgroup, user, password);
02885 
02886                         if (!srv) {
02887 
02888                                 if (dir) {
02889                                         SAFE_FREE(dir->fname);
02890                                         SAFE_FREE(dir);
02891                                 }
02892                                 return NULL;
02893 
02894                         }
02895 
02896                         dir->srv = srv;
02897 
02898                         /* Now, list the files ... */
02899 
02900                         p = path + strlen(path);
02901                         pstrcat(path, "\\*");
02902 
02903                         if (!cli_resolve_path("", srv->cli, path,
02904                                               &targetcli, targetpath))
02905                         {
02906                                 d_printf("Could not resolve %s\n", path);
02907                                 if (dir) {
02908                                         SAFE_FREE(dir->fname);
02909                                         SAFE_FREE(dir);
02910                                 }
02911                                 return NULL;
02912                         }
02913                         
02914                         if (cli_list(targetcli, targetpath,
02915                                      aDIR | aSYSTEM | aHIDDEN,
02916                                      dir_list_fn, (void *)dir) < 0) {
02917 
02918                                 if (dir) {
02919                                         SAFE_FREE(dir->fname);
02920                                         SAFE_FREE(dir);
02921                                 }
02922                                 saved_errno = smbc_errno(context, targetcli);
02923 
02924                                 if (saved_errno == EINVAL) {
02925                                     /*
02926                                      * See if they asked to opendir something
02927                                      * other than a directory.  If so, the
02928                                      * converted error value we got would have
02929                                      * been EINVAL rather than ENOTDIR.
02930                                      */
02931                                     *p = '\0'; /* restore original path */
02932 
02933                                     if (smbc_getatr(context, srv, path,
02934                                                     &mode, NULL,
02935                                                     NULL, NULL, NULL, NULL,
02936                                                     NULL) &&
02937                                         ! IS_DOS_DIR(mode)) {
02938 
02939                                         /* It is.  Correct the error value */
02940                                         saved_errno = ENOTDIR;
02941                                     }
02942                                 }
02943 
02944                                 /*
02945                                  * If there was an error and the server is no
02946                                  * good any more...
02947                                  */
02948                                 cb = &context->callbacks;
02949                                 if (cli_is_error(targetcli) &&
02950                                     (cb->check_server_fn)(context, srv)) {
02951 
02952                                         /* ... then remove it. */
02953                                         if ((cb->remove_unused_server_fn)(context,
02954                                                                           srv)) { 
02955                                                 /*
02956                                                  * We could not remove the
02957                                                  * server completely, remove
02958                                                  * it from the cache so we
02959                                                  * will not get it again. It
02960                                                  * will be removed when the
02961                                                  * last file/dir is closed.
02962                                                  */
02963                                                 (cb->remove_cached_srv_fn)(context,
02964                                                                            srv);
02965                                         }
02966                                 }
02967 
02968                                 errno = saved_errno;
02969                                 return NULL;
02970                         }
02971                 }
02972 
02973         }
02974 
02975         DLIST_ADD(context->internal->_files, dir);
02976         return dir;
02977 
02978 }
02979 
02980 /*
02981  * Routine to close a directory
02982  */
02983 
02984 static int
02985 smbc_closedir_ctx(SMBCCTX *context,
02986                   SMBCFILE *dir)
02987 {
02988 
02989         if (!context || !context->internal ||
02990             !context->internal->_initialized) {
02991 
02992                 errno = EINVAL;
02993                 return -1;
02994 
02995         }
02996 
02997         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
02998 
02999                 errno = EBADF;
03000                 return -1;
03001     
03002         }
03003 
03004         smbc_remove_dir(dir); /* Clean it up */
03005 
03006         DLIST_REMOVE(context->internal->_files, dir);
03007 
03008         if (dir) {
03009 
03010                 SAFE_FREE(dir->fname);
03011                 SAFE_FREE(dir);    /* Free the space too */
03012         }
03013 
03014         return 0;
03015 
03016 }
03017 
03018 static void
03019 smbc_readdir_internal(SMBCCTX * context,
03020                       struct smbc_dirent *dest,
03021                       struct smbc_dirent *src,
03022                       int max_namebuf_len)
03023 {
03024         if (context->options.urlencode_readdir_entries) {
03025 
03026                 /* url-encode the name.  get back remaining buffer space */
03027                 max_namebuf_len =
03028                         smbc_urlencode(dest->name, src->name, max_namebuf_len);
03029 
03030                 /* We now know the name length */
03031                 dest->namelen = strlen(dest->name);
03032 
03033                 /* Save the pointer to the beginning of the comment */
03034                 dest->comment = dest->name + dest->namelen + 1;
03035 
03036                 /* Copy the comment */
03037                 strncpy(dest->comment, src->comment, max_namebuf_len - 1);
03038                 dest->comment[max_namebuf_len - 1] = '\0';
03039 
03040                 /* Save other fields */
03041                 dest->smbc_type = src->smbc_type;
03042                 dest->commentlen = strlen(dest->comment);
03043                 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
03044                                 (char *) dest);
03045         } else {
03046 
03047                 /* No encoding.  Just copy the entry as is. */
03048                 memcpy(dest, src, src->dirlen);
03049                 dest->comment = (char *)(&dest->name + src->namelen + 1);
03050         }
03051         
03052 }
03053 
03054 /*
03055  * Routine to get a directory entry
03056  */
03057 
03058 struct smbc_dirent *
03059 smbc_readdir_ctx(SMBCCTX *context,
03060                  SMBCFILE *dir)
03061 {
03062         int maxlen;
03063         struct smbc_dirent *dirp, *dirent;
03064 
03065         /* Check that all is ok first ... */
03066 
03067         if (!context || !context->internal ||
03068             !context->internal->_initialized) {
03069 
03070                 errno = EINVAL;
03071                 DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
03072                 return NULL;
03073 
03074         }
03075 
03076         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
03077 
03078                 errno = EBADF;
03079                 DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
03080                 return NULL;
03081 
03082         }
03083 
03084         if (dir->file != False) { /* FIXME, should be dir, perhaps */
03085 
03086                 errno = ENOTDIR;
03087                 DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
03088                 return NULL;
03089 
03090         }
03091 
03092         if (!dir->dir_next) {
03093                 return NULL;
03094         }
03095 
03096         dirent = dir->dir_next->dirent;
03097         if (!dirent) {
03098 
03099                 errno = ENOENT;
03100                 return NULL;
03101 
03102         }
03103 
03104         dirp = (struct smbc_dirent *)context->internal->_dirent;
03105         maxlen = (sizeof(context->internal->_dirent) -
03106                   sizeof(struct smbc_dirent));
03107 
03108         smbc_readdir_internal(context, dirp, dirent, maxlen);
03109 
03110         dir->dir_next = dir->dir_next->next;
03111 
03112         return dirp;
03113 }
03114 
03115 /*
03116  * Routine to get directory entries
03117  */
03118 
03119 static int
03120 smbc_getdents_ctx(SMBCCTX *context,
03121                   SMBCFILE *dir,
03122                   struct smbc_dirent *dirp,
03123                   int count)
03124 {
03125         int rem = count;
03126         int reqd;
03127         int maxlen;
03128         char *ndir = (char *)dirp;
03129         struct smbc_dir_list *dirlist;
03130 
03131         /* Check that all is ok first ... */
03132 
03133         if (!context || !context->internal ||
03134             !context->internal->_initialized) {
03135 
03136                 errno = EINVAL;
03137                 return -1;
03138 
03139         }
03140 
03141         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
03142 
03143                 errno = EBADF;
03144                 return -1;
03145     
03146         }
03147 
03148         if (dir->file != False) { /* FIXME, should be dir, perhaps */
03149 
03150                 errno = ENOTDIR;
03151                 return -1;
03152 
03153         }
03154 
03155         /* 
03156          * Now, retrieve the number of entries that will fit in what was passed
03157          * We have to figure out if the info is in the list, or we need to 
03158          * send a request to the server to get the info.
03159          */
03160 
03161         while ((dirlist = dir->dir_next)) {
03162                 struct smbc_dirent *dirent;
03163 
03164                 if (!dirlist->dirent) {
03165 
03166                         errno = ENOENT;  /* Bad error */
03167                         return -1;
03168 
03169                 }
03170 
03171                 /* Do urlencoding of next entry, if so selected */
03172                 dirent = (struct smbc_dirent *)context->internal->_dirent;
03173                 maxlen = (sizeof(context->internal->_dirent) -
03174                           sizeof(struct smbc_dirent));
03175                 smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
03176 
03177                 reqd = dirent->dirlen;
03178 
03179                 if (rem < reqd) {
03180 
03181                         if (rem < count) { /* We managed to copy something */
03182 
03183                                 errno = 0;
03184                                 return count - rem;
03185 
03186                         }
03187                         else { /* Nothing copied ... */
03188 
03189                                 errno = EINVAL;  /* Not enough space ... */
03190                                 return -1;
03191 
03192                         }
03193 
03194                 }
03195 
03196                 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
03197     
03198                 ((struct smbc_dirent *)ndir)->comment = 
03199                         (char *)(&((struct smbc_dirent *)ndir)->name +
03200                                  dirent->namelen +
03201                                  1);
03202 
03203                 ndir += reqd;
03204 
03205                 rem -= reqd;
03206 
03207                 dir->dir_next = dirlist = dirlist -> next;
03208         }
03209 
03210         if (rem == count)
03211                 return 0;
03212         else 
03213                 return count - rem;
03214 
03215 }
03216 
03217 /*
03218  * Routine to create a directory ...
03219  */
03220 
03221 static int
03222 smbc_mkdir_ctx(SMBCCTX *context,
03223                const char *fname,
03224                mode_t mode)
03225 {
03226         SMBCSRV *srv;
03227         fstring server;
03228         fstring share;
03229         fstring user;
03230         fstring password;
03231         fstring workgroup;
03232         pstring path, targetpath;
03233         struct cli_state *targetcli;
03234 
03235         if (!context || !context->internal || 
03236             !context->internal->_initialized) {
03237 
03238                 errno = EINVAL;
03239                 return -1;
03240 
03241         }
03242 
03243         if (!fname) {
03244 
03245                 errno = EINVAL;
03246                 return -1;
03247 
03248         }
03249   
03250         DEBUG(4, ("smbc_mkdir(%s)\n", fname));
03251 
03252         if (smbc_parse_path(context, fname,
03253                             workgroup, sizeof(workgroup),
03254                             server, sizeof(server),
03255                             share, sizeof(share),
03256                             path, sizeof(path),
03257                             user, sizeof(user),
03258                             password, sizeof(password),
03259                             NULL, 0)) {
03260                 errno = EINVAL;
03261                 return -1;
03262         }
03263 
03264         if (user[0] == (char)0) fstrcpy(user, context->user);
03265 
03266         srv = smbc_server(context, True,
03267                           server, share, workgroup, user, password);
03268 
03269         if (!srv) {
03270 
03271                 return -1;  /* errno set by smbc_server */
03272 
03273         }
03274 
03275         /*d_printf(">>>mkdir: resolving %s\n", path);*/
03276         if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
03277         {
03278                 d_printf("Could not resolve %s\n", path);
03279                 return -1;
03280         }
03281         /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
03282 
03283         if (!cli_mkdir(targetcli, targetpath)) {
03284 
03285                 errno = smbc_errno(context, targetcli);
03286                 return -1;
03287 
03288         } 
03289 
03290         return 0;
03291 
03292 }
03293 
03294 /*
03295  * Our list function simply checks to see if a directory is not empty
03296  */
03297 
03298 static int smbc_rmdir_dirempty = True;
03299 
03300 static void
03301 rmdir_list_fn(const char *mnt,
03302               file_info *finfo,
03303               const char *mask,
03304               void *state)
03305 {
03306         if (strncmp(finfo->name, ".", 1) != 0 &&
03307             strncmp(finfo->name, "..", 2) != 0) {
03308                 
03309                 smbc_rmdir_dirempty = False;
03310         }
03311 }
03312 
03313 /*
03314  * Routine to remove a directory
03315  */
03316 
03317 static int
03318 smbc_rmdir_ctx(SMBCCTX *context,
03319                const char *fname)
03320 {
03321         SMBCSRV *srv;
03322         fstring server;
03323         fstring share;
03324         fstring user;
03325         fstring password;
03326         fstring workgroup;
03327         pstring path;
03328         pstring targetpath;
03329         struct cli_state *targetcli;
03330 
03331         if (!context || !context->internal || 
03332             !context->internal->_initialized) {
03333 
03334                 errno = EINVAL;
03335                 return -1;
03336 
03337         }
03338 
03339         if (!fname) {
03340 
03341                 errno = EINVAL;
03342                 return -1;
03343 
03344         }
03345   
03346         DEBUG(4, ("smbc_rmdir(%s)\n", fname));
03347 
03348         if (smbc_parse_path(context, fname,
03349                             workgroup, sizeof(workgroup),
03350                             server, sizeof(server),
03351                             share, sizeof(share),
03352                             path, sizeof(path),
03353                             user, sizeof(user),
03354                             password, sizeof(password),
03355                             NULL, 0))
03356         {
03357                 errno = EINVAL;
03358                 return -1;
03359         }
03360 
03361         if (user[0] == (char)0) fstrcpy(user, context->user);
03362 
03363         srv = smbc_server(context, True,
03364                           server, share, workgroup, user, password);
03365 
03366         if (!srv) {
03367 
03368                 return -1;  /* errno set by smbc_server */
03369 
03370         }
03371 
03372         /*d_printf(">>>rmdir: resolving %s\n", path);*/
03373         if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
03374         {
03375                 d_printf("Could not resolve %s\n", path);
03376                 return -1;
03377         }
03378         /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
03379 
03380 
03381         if (!cli_rmdir(targetcli, targetpath)) {
03382 
03383                 errno = smbc_errno(context, targetcli);
03384 
03385                 if (errno == EACCES) {  /* Check if the dir empty or not */
03386 
03387                         /* Local storage to avoid buffer overflows */
03388                         pstring lpath; 
03389 
03390                         smbc_rmdir_dirempty = True;  /* Make this so ... */
03391 
03392                         pstrcpy(lpath, targetpath);
03393                         pstrcat(lpath, "\\*");
03394 
03395                         if (cli_list(targetcli, lpath,
03396                                      aDIR | aSYSTEM | aHIDDEN,
03397                                      rmdir_list_fn, NULL) < 0) {
03398 
03399                                 /* Fix errno to ignore latest error ... */
03400                                 DEBUG(5, ("smbc_rmdir: "
03401                                           "cli_list returned an error: %d\n", 
03402                                           smbc_errno(context, targetcli)));
03403                                 errno = EACCES;
03404 
03405                         }
03406 
03407                         if (smbc_rmdir_dirempty)
03408                                 errno = EACCES;
03409                         else
03410                                 errno = ENOTEMPTY;
03411 
03412                 }
03413 
03414                 return -1;
03415 
03416         } 
03417 
03418         return 0;
03419 
03420 }
03421 
03422 /*
03423  * Routine to return the current directory position
03424  */
03425 
03426 static off_t
03427 smbc_telldir_ctx(SMBCCTX *context,
03428                  SMBCFILE *dir)
03429 {
03430         if (!context || !context->internal ||
03431             !context->internal->_initialized) {
03432 
03433                 errno = EINVAL;
03434                 return -1;
03435 
03436         }
03437 
03438         if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
03439 
03440                 errno = EBADF;
03441                 return -1;
03442 
03443         }
03444 
03445         if (dir->file != False) { /* FIXME, should be dir, perhaps */
03446 
03447                 errno = ENOTDIR;
03448                 return -1;
03449 
03450         }
03451 
03452         /* See if we're already at the end. */
03453         if (dir->dir_next == NULL) {
03454                 /* We are. */
03455                 return -1;
03456         }
03457 
03458         /*
03459          * We return the pointer here as the offset
03460          */
03461         return (off_t)(long)dir->dir_next->dirent;
03462 }
03463 
03464 /*
03465  * A routine to run down the list and see if the entry is OK
03466  */
03467 
03468 struct smbc_dir_list *
03469 smbc_check_dir_ent(struct smbc_dir_list *list, 
03470                    struct smbc_dirent *dirent)
03471 {
03472 
03473         /* Run down the list looking for what we want */
03474 
03475         if (dirent) {
03476 
03477                 struct smbc_dir_list *tmp = list;
03478 
03479                 while (tmp) {
03480 
03481                         if (tmp->dirent == dirent)
03482                                 return tmp;
03483 
03484                         tmp = tmp->next;
03485 
03486                 }
03487 
03488         }
03489 
03490         return NULL;  /* Not found, or an error */
03491 
03492 }
03493 
03494 
03495 /*
03496  * Routine to seek on a directory
03497  */
03498 
03499 static int
03500 smbc_lseekdir_ctx(SMBCCTX *context,
03501                   SMBCFILE *dir,
03502                   off_t offset)
03503 {
03504         long int l_offset = offset;  /* Handle problems of size */
03505         struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
03506         struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
03507 
03508         if (!context || !context->internal ||
03509             !context->internal->_initialized) {
03510 
03511                 errno = EINVAL;
03512                 return -1;
03513 
03514         }
03515 
03516         if (dir->file != False) { /* FIXME, should be dir, perhaps */
03517 
03518                 errno = ENOTDIR;
03519                 return -1;
03520 
03521         }
03522 
03523         /* Now, check what we were passed and see if it is OK ... */
03524 
03525         if (dirent == NULL) {  /* Seek to the begining of the list */
03526 
03527                 dir->dir_next = dir->dir_list;
03528                 return 0;
03529 
03530         }
03531 
03532         if (offset == -1) {     /* Seek to the end of the list */
03533                 dir->dir_next = NULL;
03534                 return 0;
03535         }
03536 
03537         /* Now, run down the list and make sure that the entry is OK       */
03538         /* This may need to be changed if we change the format of the list */
03539 
03540         if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
03541 
03542                 errno = EINVAL;   /* Bad entry */
03543                 return -1;
03544 
03545         }
03546 
03547         dir->dir_next = list_ent;
03548 
03549         return 0; 
03550 
03551 }
03552 
03553 /*
03554  * Routine to fstat a dir
03555  */
03556 
03557 static int
03558 smbc_fstatdir_ctx(SMBCCTX *context,
03559                   SMBCFILE *dir,
03560                   struct stat *st)
03561 {
03562 
03563         if (!context || !context->internal || 
03564             !context->internal->_initialized) {
03565 
03566                 errno = EINVAL;
03567                 return -1;
03568 
03569         }
03570 
03571         /* No code yet ... */
03572 
03573         return 0;
03574 
03575 }
03576 
03577 static int
03578 smbc_chmod_ctx(SMBCCTX *context,
03579                const char *fname,
03580                mode_t newmode)
03581 {
03582         SMBCSRV *srv;
03583         fstring server;
03584         fstring share;
03585         fstring user;
03586         fstring password;
03587         fstring workgroup;
03588         pstring path;
03589         uint16 mode;
03590 
03591         if (!context || !context->internal ||
03592             !context->internal->_initialized) {
03593 
03594                 errno = EINVAL;  /* Best I can think of ... */
03595                 return -1;
03596     
03597         }
03598 
03599         if (!fname) {
03600 
03601                 errno = EINVAL;
03602                 return -1;
03603 
03604         }
03605   
03606         DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
03607 
03608         if (smbc_parse_path(context, fname,
03609                             workgroup, sizeof(workgroup),
03610                             server, sizeof(server),
03611                             share, sizeof(share),
03612                             path, sizeof(path),
03613                             user, sizeof(user),
03614                             password, sizeof(password),
03615                             NULL, 0)) {
03616                 errno = EINVAL;
03617                 return -1;
03618         }
03619 
03620         if (user[0] == (char)0) fstrcpy(user, context->user);
03621 
03622         srv = smbc_server(context, True,
03623                           server, share, workgroup, user, password);
03624 
03625         if (!srv) {
03626                 return -1;  /* errno set by smbc_server */
03627         }
03628 
03629         mode = 0;
03630 
03631         if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
03632         if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
03633         if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
03634         if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
03635 
03636         if (!cli_setatr(srv->cli, path, mode, 0)) {
03637                 errno = smbc_errno(context, srv->cli);
03638                 return -1;
03639         }
03640         
03641         return 0;
03642 }
03643 
03644 static int
03645 smbc_utimes_ctx(SMBCCTX *context,
03646                 const char *fname,
03647                 struct timeval *tbuf)
03648 {
03649         SMBCSRV *srv;
03650         fstring server;
03651         fstring share;
03652         fstring user;
03653         fstring password;
03654         fstring workgroup;
03655         pstring path;
03656         time_t access_time;
03657         time_t write_time;
03658 
03659         if (!context || !context->internal ||
03660             !context->internal->_initialized) {
03661 
03662                 errno = EINVAL;  /* Best I can think of ... */
03663                 return -1;
03664     
03665         }
03666 
03667         if (!fname) {
03668 
03669                 errno = EINVAL;
03670                 return -1;
03671 
03672         }
03673   
03674         if (tbuf == NULL) {
03675                 access_time = write_time = time(NULL);
03676         } else {
03677                 access_time = tbuf[0].tv_sec;
03678                 write_time = tbuf[1].tv_sec;
03679         }
03680 
03681         if (DEBUGLVL(4)) 
03682         {
03683                 char *p;
03684                 char atimebuf[32];
03685                 char mtimebuf[32];
03686 
03687                 strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
03688                 atimebuf[sizeof(atimebuf) - 1] = '\0';
03689                 if ((p = strchr(atimebuf, '\n')) != NULL) {
03690                         *p = '\0';
03691                 }
03692 
03693                 strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
03694                 mtimebuf[sizeof(mtimebuf) - 1] = '\0';
03695                 if ((p = strchr(mtimebuf, '\n')) != NULL) {
03696                         *p = '\0';
03697                 }
03698 
03699                 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
03700                         fname, atimebuf, mtimebuf);
03701         }
03702 
03703         if (smbc_parse_path(context, fname,
03704                             workgroup, sizeof(workgroup),
03705                             server, sizeof(server),
03706                             share, sizeof(share),
03707                             path, sizeof(path),
03708                             user, sizeof(user),
03709                             password, sizeof(password),
03710                             NULL, 0)) {
03711                 errno = EINVAL;
03712                 return -1;
03713         }
03714 
03715         if (user[0] == (char)0) fstrcpy(user, context->user);
03716 
03717         srv = smbc_server(context, True,
03718                           server, share, workgroup, user, password);
03719 
03720         if (!srv) {
03721                 return -1;      /* errno set by smbc_server */
03722         }
03723 
03724         if (!smbc_setatr(context, srv, path,
03725                          0, access_time, write_time, 0, 0)) {
03726                 return -1;      /* errno set by smbc_setatr */
03727         }
03728 
03729         return 0;
03730 }
03731 
03732 
03733 /*
03734  * Sort ACEs according to the documentation at
03735  * http://support.microsoft.com/kb/269175, at least as far as it defines the
03736  * order.
03737  */
03738 
03739 static int
03740 ace_compare(SEC_ACE *ace1,
03741             SEC_ACE *ace2)
03742 {
03743         BOOL b1;
03744         BOOL b2;
03745 
03746         /* If the ACEs are equal, we have nothing more to do. */
03747         if (sec_ace_equal(ace1, ace2)) {
03748                 return 0;
03749         }
03750 
03751         /* Inherited follow non-inherited */
03752         b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
03753         b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
03754         if (b1 != b2) {
03755                 return (b1 ? 1 : -1);
03756         }
03757 
03758         /*
03759          * What shall we do with AUDITs and ALARMs?  It's undefined.  We'll
03760          * sort them after DENY and ALLOW.
03761          */
03762         b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
03763               ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
03764               ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
03765               ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
03766         b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
03767               ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
03768               ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
03769               ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
03770         if (b1 != b2) {
03771                 return (b1 ? 1 : -1);
03772         }
03773 
03774         /* Allowed ACEs follow denied ACEs */
03775         b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
03776               ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
03777         b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
03778               ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
03779         if (b1 != b2) {
03780                 return (b1 ? 1 : -1);
03781         }
03782 
03783         /*
03784          * ACEs applying to an entity's object follow those applying to the
03785          * entity itself
03786          */
03787         b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
03788               ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
03789         b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
03790               ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
03791         if (b1 != b2) {
03792                 return (b1 ? 1 : -1);
03793         }
03794 
03795         /*
03796          * If we get this far, the ACEs are similar as far as the
03797          * characteristics we typically care about (those defined by the
03798          * referenced MS document).  We'll now sort by characteristics that
03799          * just seems reasonable.
03800          */
03801         
03802         if (ace1->type != ace2->type) {
03803                 return ace2->type - ace1->type;
03804         }
03805 
03806         if (sid_compare(&ace1->trustee, &ace2->trustee)) {
03807                 return sid_compare(&ace1->trustee, &ace2->trustee);
03808         }
03809 
03810         if (ace1->flags != ace2->flags) {
03811                 return ace1->flags - ace2->flags;
03812         }
03813 
03814         if (ace1->access_mask != ace2->access_mask) {
03815                 return ace1->access_mask - ace2->access_mask;
03816         }
03817 
03818         if (ace1->size != ace2->size) {
03819                 return ace1->size - ace2->size;
03820         }
03821 
03822         return memcmp(ace1, ace2, sizeof(SEC_ACE));
03823 }
03824 
03825 
03826 static void
03827 sort_acl(SEC_ACL *the_acl)
03828 {
03829         uint32 i;
03830         if (!the_acl) return;
03831 
03832         qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
03833               QSORT_CAST ace_compare);
03834 
03835         for (i=1;i<the_acl->num_aces;) {
03836                 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
03837                         int j;
03838                         for (j=i; j<the_acl->num_aces-1; j++) {
03839                                 the_acl->aces[j] = the_acl->aces[j+1];
03840                         }
03841                         the_acl->num_aces--;
03842                 } else {
03843                         i++;
03844                 }
03845         }
03846 }
03847 
03848 /* convert a SID to a string, either numeric or username/group */
03849 static void
03850 convert_sid_to_string(struct cli_state *ipc_cli,
03851                       POLICY_HND *pol,
03852                       fstring str,
03853                       BOOL numeric,
03854                       DOM_SID *sid)
03855 {
03856         char **domains = NULL;
03857         char **names = NULL;
03858         enum lsa_SidType *types = NULL;
03859         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
03860         sid_to_string(str, sid);
03861 
03862         if (numeric) {
03863                 return;     /* no lookup desired */
03864         }
03865        
03866         if (!pipe_hnd) {
03867                 return;
03868         }
03869  
03870         /* Ask LSA to convert the sid to a name */
03871 
03872         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,  
03873                                                  pol, 1, sid, &domains, 
03874                                                  &names, &types)) ||
03875             !domains || !domains[0] || !names || !names[0]) {
03876                 return;
03877         }
03878 
03879         /* Converted OK */
03880 
03881         slprintf(str, sizeof(fstring) - 1, "%s%s%s",
03882                  domains[0], lp_winbind_separator(),
03883                  names[0]);
03884 }
03885 
03886 /* convert a string to a SID, either numeric or username/group */
03887 static BOOL
03888 convert_string_to_sid(struct cli_state *ipc_cli,
03889                       POLICY_HND *pol,
03890                       BOOL numeric,
03891                       DOM_SID *sid,
03892                       const char *str)
03893 {
03894         enum lsa_SidType *types = NULL;
03895         DOM_SID *sids = NULL;
03896         BOOL result = True;
03897         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
03898 
03899         if (!pipe_hnd) {
03900                 return False;
03901         }
03902 
03903         if (numeric) {
03904                 if (strncmp(str, "S-", 2) == 0) {
03905                         return string_to_sid(sid, str);
03906                 }
03907 
03908                 result = False;
03909                 goto done;
03910         }
03911 
03912         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx, 
03913                                                   pol, 1, &str, NULL, &sids, 
03914                                                   &types))) {
03915                 result = False;
03916                 goto done;
03917         }
03918 
03919         sid_copy(sid, &sids[0]);
03920  done:
03921 
03922         return result;
03923 }
03924 
03925 
03926 /* parse an ACE in the same format as print_ace() */
03927 static BOOL
03928 parse_ace(struct cli_state *ipc_cli,
03929           POLICY_HND *pol,
03930           SEC_ACE *ace,
03931           BOOL numeric,
03932           char *str)
03933 {
03934         char *p;
03935         const char *cp;
03936         fstring tok;
03937         unsigned int atype;
03938         unsigned int aflags;
03939         unsigned int amask;
03940         DOM_SID sid;
03941         SEC_ACCESS mask;
03942         const struct perm_value *v;
03943         struct perm_value {
03944                 const char *perm;
03945                 uint32 mask;
03946         };
03947 
03948         /* These values discovered by inspection */
03949         static const struct perm_value special_values[] = {
03950                 { "R", 0x00120089 },
03951                 { "W", 0x00120116 },
03952                 { "X", 0x001200a0 },
03953                 { "D", 0x00010000 },
03954                 { "P", 0x00040000 },
03955                 { "O", 0x00080000 },
03956                 { NULL, 0 },
03957         };
03958 
03959         static const struct perm_value standard_values[] = {
03960                 { "READ",   0x001200a9 },
03961                 { "CHANGE", 0x001301bf },
03962                 { "FULL",   0x001f01ff },
03963                 { NULL, 0 },
03964         };
03965 
03966 
03967         ZERO_STRUCTP(ace);
03968         p = strchr_m(str,':');
03969         if (!p) return False;
03970         *p = '\0';
03971         p++;
03972         /* Try to parse numeric form */
03973 
03974         if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
03975             convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
03976                 goto done;
03977         }
03978 
03979         /* Try to parse text form */
03980 
03981         if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
03982                 return False;
03983         }
03984 
03985         cp = p;
03986         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
03987                 return False;
03988         }
03989 
03990         if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
03991                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
03992         } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
03993                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
03994         } else {
03995                 return False;
03996         }
03997 
03998         /* Only numeric form accepted for flags at present */
03999 
04000         if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
04001               sscanf(tok, "%i", &aflags))) {
04002                 return False;
04003         }
04004 
04005         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
04006                 return False;
04007         }
04008 
04009         if (strncmp(tok, "0x", 2) == 0) {
04010                 if (sscanf(tok, "%i", &amask) != 1) {
04011                         return False;
04012                 }
04013                 goto done;
04014         }
04015 
04016         for (v = standard_values; v->perm; v++) {
04017                 if (strcmp(tok, v->perm) == 0) {
04018                         amask = v->mask;
04019                         goto done;
04020                 }
04021         }
04022 
04023         p = tok;
04024 
04025         while(*p) {
04026                 BOOL found = False;
04027 
04028                 for (v = special_values; v->perm; v++) {
04029                         if (v->perm[0] == *p) {
04030                                 amask |= v->mask;
04031                                 found = True;
04032                         }
04033                 }
04034 
04035                 if (!found) return False;
04036                 p++;
04037         }
04038 
04039         if (*p) {
04040                 return False;
04041         }
04042 
04043  done:
04044         mask = amask;
04045         init_sec_ace(ace, &sid, atype, mask, aflags);
04046         return True;
04047 }
04048 
04049 /* add an ACE to a list of ACEs in a SEC_ACL */
04050 static BOOL
04051 add_ace(SEC_ACL **the_acl,
04052         SEC_ACE *ace,
04053         TALLOC_CTX *ctx)
04054 {
04055         SEC_ACL *newacl;
04056         SEC_ACE *aces;
04057 
04058         if (! *the_acl) {
04059                 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
04060                 return True;
04061         }
04062 
04063         if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
04064                 return False;
04065         }
04066         memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
04067         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
04068         newacl = make_sec_acl(ctx, (*the_acl)->revision,
04069                               1+(*the_acl)->num_aces, aces);
04070         SAFE_FREE(aces);
04071         (*the_acl) = newacl;
04072         return True;
04073 }
04074 
04075 
04076 /* parse a ascii version of a security descriptor */
04077 static SEC_DESC *
04078 sec_desc_parse(TALLOC_CTX *ctx,
04079                struct cli_state *ipc_cli,
04080                POLICY_HND *pol,
04081                BOOL numeric,
04082                char *str)
04083 {
04084         const char *p = str;
04085         fstring tok;
04086         SEC_DESC *ret = NULL;
04087         size_t sd_size;
04088         DOM_SID *group_sid=NULL;
04089         DOM_SID *owner_sid=NULL;
04090         SEC_ACL *dacl=NULL;
04091         int revision=1;
04092 
04093         while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
04094 
04095                 if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
04096                         revision = strtol(tok+9, NULL, 16);
04097                         continue;
04098                 }
04099 
04100                 if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
04101                         if (owner_sid) {
04102                                 DEBUG(5, ("OWNER specified more than once!\n"));
04103                                 goto done;
04104                         }
04105                         owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
04106                         if (!owner_sid ||
04107                             !convert_string_to_sid(ipc_cli, pol,
04108                                                    numeric,
04109                                                    owner_sid, tok+6)) {
04110                                 DEBUG(5, ("Failed to parse owner sid\n"));
04111                                 goto done;
04112                         }
04113                         continue;
04114                 }
04115 
04116                 if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
04117                         if (owner_sid) {
04118                                 DEBUG(5, ("OWNER specified more than once!\n"));
04119                                 goto done;
04120                         }
04121                         owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
04122                         if (!owner_sid ||
04123                             !convert_string_to_sid(ipc_cli, pol,
04124                                                    False,
04125                                                    owner_sid, tok+7)) {
04126                                 DEBUG(5, ("Failed to parse owner sid\n"));
04127                                 goto done;
04128                         }
04129                         continue;
04130                 }
04131 
04132                 if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
04133                         if (group_sid) {
04134                                 DEBUG(5, ("GROUP specified more than once!\n"));
04135                                 goto done;
04136                         }
04137                         group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
04138                         if (!group_sid ||
04139                             !convert_string_to_sid(ipc_cli, pol,
04140                                                    numeric,
04141                                                    group_sid, tok+6)) {
04142                                 DEBUG(5, ("Failed to parse group sid\n"));
04143                                 goto done;
04144                         }
04145                         continue;
04146                 }
04147 
04148                 if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
04149                         if (group_sid) {
04150                                 DEBUG(5, ("GROUP specified more than once!\n"));
04151                                 goto done;
04152                         }
04153                         group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
04154                         if (!group_sid ||
04155                             !convert_string_to_sid(ipc_cli, pol,
04156                                                    False,
04157                                                    group_sid, tok+6)) {
04158                                 DEBUG(5, ("Failed to parse group sid\n"));
04159                                 goto done;
04160                         }
04161                         continue;
04162                 }
04163 
04164                 if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
04165                         SEC_ACE ace;
04166                         if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
04167                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
04168                                 goto done;
04169                         }
04170                         if(!add_ace(&dacl, &ace, ctx)) {
04171                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
04172                                 goto done;
04173                         }
04174                         continue;
04175                 }
04176 
04177                 if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
04178                         SEC_ACE ace;
04179                         if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
04180                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
04181                                 goto done;
04182                         }
04183                         if(!add_ace(&dacl, &ace, ctx)) {
04184                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
04185                                 goto done;
04186                         }
04187                         continue;
04188                 }
04189 
04190                 DEBUG(5, ("Failed to parse security descriptor\n"));
04191                 goto done;
04192         }
04193 
04194         ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE, 
04195                             owner_sid, group_sid, NULL, dacl, &sd_size);
04196 
04197   done:
04198         SAFE_FREE(group_sid);
04199         SAFE_FREE(owner_sid);
04200 
04201         return ret;
04202 }
04203 
04204 
04205 /* Obtain the current dos attributes */
04206 static DOS_ATTR_DESC *
04207 dos_attr_query(SMBCCTX *context,
04208                TALLOC_CTX *ctx,
04209                const char *filename,
04210                SMBCSRV *srv)
04211 {
04212         struct timespec create_time_ts;
04213         struct timespec write_time_ts;
04214         struct timespec access_time_ts;
04215         struct timespec change_time_ts;
04216         SMB_OFF_T size = 0;
04217         uint16 mode = 0;
04218         SMB_INO_T inode = 0;
04219         DOS_ATTR_DESC *ret;
04220     
04221         ret = TALLOC_P(ctx, DOS_ATTR_DESC);
04222         if (!ret) {
04223                 errno = ENOMEM;
04224                 return NULL;
04225         }
04226 
04227         /* Obtain the DOS attributes */
04228         if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
04229                          &mode, &size, 
04230                          &create_time_ts,
04231                          &access_time_ts,
04232                          &write_time_ts,
04233                          &change_time_ts, 
04234                          &inode)) {
04235         
04236                 errno = smbc_errno(context, srv->cli);
04237                 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
04238                 return NULL;
04239         
04240         }
04241                 
04242         ret->mode = mode;
04243         ret->size = size;
04244         ret->create_time = convert_timespec_to_time_t(create_time_ts);
04245         ret->access_time = convert_timespec_to_time_t(access_time_ts);
04246         ret->write_time = convert_timespec_to_time_t(write_time_ts);
04247         ret->change_time = convert_timespec_to_time_t(change_time_ts);
04248         ret->inode = inode;
04249 
04250         return ret;
04251 }
04252 
04253 
04254 /* parse a ascii version of a security descriptor */
04255 static void
04256 dos_attr_parse(SMBCCTX *context,
04257                DOS_ATTR_DESC *dad,
04258                SMBCSRV *srv,
04259                char *str)
04260 {
04261         int n;
04262         const char *p = str;
04263         fstring tok;
04264         struct {
04265                 const char * create_time_attr;
04266                 const char * access_time_attr;
04267                 const char * write_time_attr;
04268                 const char * change_time_attr;
04269         } attr_strings;
04270 
04271         /* Determine whether to use old-style or new-style attribute names */
04272         if (context->internal->_full_time_names) {
04273                 /* new-style names */
04274                 attr_strings.create_time_attr = "CREATE_TIME";
04275                 attr_strings.access_time_attr = "ACCESS_TIME";
04276                 attr_strings.write_time_attr = "WRITE_TIME";
04277                 attr_strings.change_time_attr = "CHANGE_TIME";
04278         } else {
04279                 /* old-style names */
04280                 attr_strings.create_time_attr = NULL;
04281                 attr_strings.access_time_attr = "A_TIME";
04282                 attr_strings.write_time_attr = "M_TIME";
04283                 attr_strings.change_time_attr = "C_TIME";
04284         }
04285 
04286         /* if this is to set the entire ACL... */
04287         if (*str == '*') {
04288                 /* ... then increment past the first colon if there is one */
04289                 if ((p = strchr(str, ':')) != NULL) {
04290                         ++p;
04291                 } else {
04292                         p = str;
04293                 }
04294         }
04295 
04296         while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
04297 
04298                 if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
04299                         dad->mode = strtol(tok+5, NULL, 16);
04300                         continue;
04301                 }
04302 
04303                 if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
04304                         dad->size = (SMB_OFF_T)atof(tok+5);
04305                         continue;
04306                 }
04307 
04308                 n = strlen(attr_strings.access_time_attr);
04309                 if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
04310                         dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
04311                         continue;
04312                 }
04313 
04314                 n = strlen(attr_strings.change_time_attr);
04315                 if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
04316                         dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
04317                         continue;
04318                 }
04319 
04320                 n = strlen(attr_strings.write_time_attr);
04321                 if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
04322                         dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
04323                         continue;
04324                 }
04325 
04326                 if (attr_strings.create_time_attr != NULL) {
04327                         n = strlen(attr_strings.create_time_attr);
04328                         if (StrnCaseCmp(tok, attr_strings.create_time_attr,
04329                                         n) == 0) {
04330                                 dad->create_time = (time_t)strtol(tok+n+1,
04331                                                                   NULL, 10);
04332                                 continue;
04333                         }
04334                 }
04335 
04336                 if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
04337                         dad->inode = (SMB_INO_T)atof(tok+6);
04338                         continue;
04339                 }
04340         }
04341 }
04342 
04343 /***************************************************** 
04344  Retrieve the acls for a file.
04345 *******************************************************/
04346 
04347 static int
04348 cacl_get(SMBCCTX *context,
04349          TALLOC_CTX *ctx,
04350          SMBCSRV *srv,
04351          struct cli_state *ipc_cli,
04352          POLICY_HND *pol,
04353          char *filename,
04354          char *attr_name,
04355          char *buf,
04356          int bufsize)
04357 {
04358         uint32 i;
04359         int n = 0;
04360         int n_used;
04361         BOOL all;
04362         BOOL all_nt;
04363         BOOL all_nt_acls;
04364         BOOL all_dos;
04365         BOOL some_nt;
04366         BOOL some_dos;
04367         BOOL exclude_nt_revision = False;
04368         BOOL exclude_nt_owner = False;
04369         BOOL exclude_nt_group = False;
04370         BOOL exclude_nt_acl = False;
04371         BOOL exclude_dos_mode = False;
04372         BOOL exclude_dos_size = False;
04373         BOOL exclude_dos_create_time = False;
04374         BOOL exclude_dos_access_time = False;
04375         BOOL exclude_dos_write_time = False;
04376         BOOL exclude_dos_change_time = False;
04377         BOOL exclude_dos_inode = False;
04378         BOOL numeric = True;
04379         BOOL determine_size = (bufsize == 0);
04380         int fnum = -1;
04381         SEC_DESC *sd;
04382         fstring sidstr;
04383         fstring name_sandbox;
04384         char *name;
04385         char *pExclude;
04386         char *p;
04387         struct timespec create_time_ts;
04388         struct timespec write_time_ts;
04389         struct timespec access_time_ts;
04390         struct timespec change_time_ts;
04391         time_t create_time = (time_t)0;
04392         time_t write_time = (time_t)0;
04393         time_t access_time = (time_t)0;
04394         time_t change_time = (time_t)0;
04395         SMB_OFF_T size = 0;
04396         uint16 mode = 0;
04397         SMB_INO_T ino = 0;
04398         struct cli_state *cli = srv->cli;
04399         struct {
04400                 const char * create_time_attr;
04401                 const char * access_time_attr;
04402                 const char * write_time_attr;
04403                 const char * change_time_attr;
04404         } attr_strings;
04405         struct {
04406                 const char * create_time_attr;
04407                 const char * access_time_attr;
04408                 const char * write_time_attr;
04409                 const char * change_time_attr;
04410         } excl_attr_strings;
04411 
04412         /* Determine whether to use old-style or new-style attribute names */
04413         if (context->internal->_full_time_names) {
04414                 /* new-style names */
04415                 attr_strings.create_time_attr = "CREATE_TIME";
04416                 attr_strings.access_time_attr = "ACCESS_TIME";
04417                 attr_strings.write_time_attr = "WRITE_TIME";
04418                 attr_strings.change_time_attr = "CHANGE_TIME";
04419 
04420                 excl_attr_strings.create_time_attr = "CREATE_TIME";
04421                 excl_attr_strings.access_time_attr = "ACCESS_TIME";
04422                 excl_attr_strings.write_time_attr = "WRITE_TIME";
04423                 excl_attr_strings.change_time_attr = "CHANGE_TIME";
04424         } else {
04425                 /* old-style names */
04426                 attr_strings.create_time_attr = NULL;
04427                 attr_strings.access_time_attr = "A_TIME";
04428                 attr_strings.write_time_attr = "M_TIME";
04429                 attr_strings.change_time_attr = "C_TIME";
04430 
04431                 excl_attr_strings.create_time_attr = NULL;
04432                 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
04433                 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
04434                 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
04435         }
04436 
04437         /* Copy name so we can strip off exclusions (if any are specified) */
04438         strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
04439 
04440         /* Ensure name is null terminated */
04441         name_sandbox[sizeof(name_sandbox) - 1] = '\0';
04442 
04443         /* Play in the sandbox */
04444         name = name_sandbox;
04445 
04446         /* If there are any exclusions, point to them and mask them from name */
04447         if ((pExclude = strchr(name, '!')) != NULL)
04448         {
04449                 *pExclude++ = '\0';
04450         }
04451 
04452         all = (StrnCaseCmp(name, "system.*", 8) == 0);
04453         all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
04454         all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
04455         all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
04456         some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
04457         some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
04458         numeric = (* (name + strlen(name) - 1) != '+');
04459 
04460         /* Look for exclusions from "all" requests */
04461         if (all || all_nt || all_dos) {
04462 
04463                 /* Exclusions are delimited by '!' */
04464                 for (;
04465                      pExclude != NULL;
04466                      pExclude = (p == NULL ? NULL : p + 1)) {
04467 
04468                 /* Find end of this exclusion name */
04469                 if ((p = strchr(pExclude, '!')) != NULL)
04470                 {
04471                     *p = '\0';
04472                 }
04473 
04474                 /* Which exclusion name is this? */
04475                 if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
04476                     exclude_nt_revision = True;
04477                 }
04478                 else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
04479                     exclude_nt_owner = True;
04480                 }
04481                 else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
04482                     exclude_nt_group = True;
04483                 }
04484                 else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
04485                     exclude_nt_acl = True;
04486                 }
04487                 else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
04488                     exclude_dos_mode = True;
04489                 }
04490                 else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
04491                     exclude_dos_size = True;
04492                 }
04493                 else if (excl_attr_strings.create_time_attr != NULL &&
04494                          StrCaseCmp(pExclude,
04495                                     excl_attr_strings.change_time_attr) == 0) {
04496                     exclude_dos_create_time = True;
04497                 }
04498                 else if (StrCaseCmp(pExclude,
04499                                     excl_attr_strings.access_time_attr) == 0) {
04500                     exclude_dos_access_time = True;
04501                 }
04502                 else if (StrCaseCmp(pExclude,
04503                                     excl_attr_strings.write_time_attr) == 0) {
04504                     exclude_dos_write_time = True;
04505                 }
04506                 else if (StrCaseCmp(pExclude,
04507                                     excl_attr_strings.change_time_attr) == 0) {
04508                     exclude_dos_change_time = True;
04509                 }
04510                 else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
04511                     exclude_dos_inode = True;
04512                 }
04513                 else {
04514                     DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
04515                               pExclude));
04516                     errno = ENOATTR;
04517                     return -1;
04518                 }
04519             }
04520         }
04521 
04522         n_used = 0;
04523 
04524         /*
04525          * If we are (possibly) talking to an NT or new system and some NT
04526          * attributes have been requested...
04527          */
04528         if (ipc_cli && (all || some_nt || all_nt_acls)) {
04529                 /* Point to the portion after "system.nt_sec_desc." */
04530                 name += 19;     /* if (all) this will be invalid but unused */
04531 
04532                 /* ... then obtain any NT attributes which were requested */
04533                 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
04534 
04535                 if (fnum == -1) {
04536                         DEBUG(5, ("cacl_get failed to open %s: %s\n",
04537                                   filename, cli_errstr(cli)));
04538                         errno = 0;
04539                         return -1;
04540                 }
04541 
04542                 sd = cli_query_secdesc(cli, fnum, ctx);
04543 
04544                 if (!sd) {
04545                         DEBUG(5,
04546                               ("cacl_get Failed to query old descriptor\n"));
04547                         errno = 0;
04548                         return -1;
04549                 }
04550 
04551                 cli_close(cli, fnum);
04552 
04553                 if (! exclude_nt_revision) {
04554                         if (all || all_nt) {
04555                                 if (determine_size) {
04556                                         p = talloc_asprintf(ctx,
04557                                                             "REVISION:%d",
04558                                                             sd->revision);
04559                                         if (!p) {
04560                                                 errno = ENOMEM;
04561                                                 return -1;
04562                                         }
04563                                         n = strlen(p);
04564                                 } else {
04565                                         n = snprintf(buf, bufsize,
04566                                                      "REVISION:%d",
04567                                                      sd->revision);
04568                                 }
04569                         } else if (StrCaseCmp(name, "revision") == 0) {
04570                                 if (determine_size) {
04571                                         p = talloc_asprintf(ctx, "%d",
04572                                                             sd->revision);
04573                                         if (!p) {
04574                                                 errno = ENOMEM;
04575                                                 return -1;
04576                                         }
04577                                         n = strlen(p);
04578                                 } else {
04579                                         n = snprintf(buf, bufsize, "%d",
04580                                                      sd->revision);
04581                                 }
04582                         }
04583         
04584                         if (!determine_size && n > bufsize) {
04585                                 errno = ERANGE;
04586                                 return -1;
04587                         }
04588                         buf += n;
04589                         n_used += n;
04590                         bufsize -= n;
04591                         n = 0;
04592                 }
04593 
04594                 if (! exclude_nt_owner) {
04595                         /* Get owner and group sid */
04596                         if (sd->owner_sid) {
04597                                 convert_sid_to_string(ipc_cli, pol,
04598                                                       sidstr,
04599                                                       numeric,
04600                                                       sd->owner_sid);
04601                         } else {
04602                                 fstrcpy(sidstr, "");
04603                         }
04604 
04605                         if (all || all_nt) {
04606                                 if (determine_size) {
04607                                         p = talloc_asprintf(ctx, ",OWNER:%s",
04608                                                             sidstr);
04609                                         if (!p) {
04610                                                 errno = ENOMEM;
04611                                                 return -1;
04612                                         }
04613                                         n = strlen(p);
04614                                 } else if (sidstr[0] != '\0') {
04615                                         n = snprintf(buf, bufsize,
04616                                                      ",OWNER:%s", sidstr);
04617                                 }
04618                         } else if (StrnCaseCmp(name, "owner", 5) == 0) {
04619                                 if (determine_size) {
04620                                         p = talloc_asprintf(ctx, "%s", sidstr);
04621                                         if (!p) {
04622                                                 errno = ENOMEM;
04623                                                 return -1;
04624                                         }
04625                                         n = strlen(p);
04626                                 } else {
04627                                         n = snprintf(buf, bufsize, "%s",
04628                                                      sidstr);
04629                                 }
04630                         }
04631 
04632                         if (!determine_size && n > bufsize) {
04633                                 errno = ERANGE;
04634                                 return -1;
04635                         }
04636                         buf += n;
04637                         n_used += n;
04638                         bufsize -= n;
04639                         n = 0;
04640                 }
04641 
04642                 if (! exclude_nt_group) {
04643                         if (sd->group_sid) {
04644                                 convert_sid_to_string(ipc_cli, pol,
04645                                                       sidstr, numeric,
04646                                                       sd->group_sid);
04647                         } else {
04648                                 fstrcpy(sidstr, "");
04649                         }
04650 
04651                         if (all || all_nt) {
04652                                 if (determine_size) {
04653                                         p = talloc_asprintf(ctx, ",GROUP:%s",
04654                                                             sidstr);
04655                                         if (!p) {
04656                                                 errno = ENOMEM;
04657                                                 return -1;
04658                                         }
04659                                         n = strlen(p);
04660                                 } else if (sidstr[0] != '\0') {
04661                                         n = snprintf(buf, bufsize,
04662                                                      ",GROUP:%s", sidstr);
04663                                 }
04664                         } else if (StrnCaseCmp(name, "group", 5) == 0) {
04665                                 if (determine_size) {
04666                                         p = talloc_asprintf(ctx, "%s", sidstr);
04667                                         if (!p) {
04668                                                 errno = ENOMEM;
04669                                                 return -1;
04670                                         }
04671                                         n = strlen(p);
04672                                 } else {
04673                                         n = snprintf(buf, bufsize,
04674                                                      "%s", sidstr);
04675                                 }
04676                         }
04677 
04678                         if (!determine_size && n > bufsize) {
04679                                 errno = ERANGE;
04680                                 return -1;
04681                         }
04682                         buf += n;
04683                         n_used += n;
04684                         bufsize -= n;
04685                         n = 0;
04686                 }
04687 
04688                 if (! exclude_nt_acl) {
04689                         /* Add aces to value buffer  */
04690                         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
04691 
04692                                 SEC_ACE *ace = &sd->dacl->aces[i];
04693                                 convert_sid_to_string(ipc_cli, pol,
04694                                                       sidstr, numeric,
04695                                                       &ace->trustee);
04696 
04697                                 if (all || all_nt) {
04698                                         if (determine_size) {
04699                                                 p = talloc_asprintf(
04700                                                         ctx, 
04701                                                         ",ACL:"
04702                                                         "%s:%d/%d/0x%08x", 
04703                                                         sidstr,
04704                                                         ace->type,
04705                                                         ace->flags,
04706                                                         ace->access_mask);
04707                                                 if (!p) {
04708                                                         errno = ENOMEM;
04709                                                         return -1;
04710                                                 }
04711                                                 n = strlen(p);
04712                                         } else {
04713                                                 n = snprintf(
04714                                                         buf, bufsize,
04715                                                         ",ACL:%s:%d/%d/0x%08x", 
04716                                                         sidstr,
04717                                                         ace->type,
04718                                                         ace->flags,
04719                                                         ace->access_mask);
04720                                         }
04721                                 } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
04722                                             StrCaseCmp(name+3, sidstr) == 0) ||
04723                                            (StrnCaseCmp(name, "acl+", 4) == 0 &&
04724                                             StrCaseCmp(name+4, sidstr) == 0)) {
04725                                         if (determine_size) {
04726                                                 p = talloc_asprintf(
04727                                                         ctx, 
04728                                                         "%d/%d/0x%08x", 
04729                                                         ace->type,
04730                                                         ace->flags,
04731                                                         ace->access_mask);
04732                                                 if (!p) {
04733                                                         errno = ENOMEM;
04734                                                         return -1;
04735                                                 }
04736                                                 n = strlen(p);
04737                                         } else {
04738                                                 n = snprintf(buf, bufsize,
04739                                                              "%d/%d/0x%08x", 
04740                                                              ace->type,
04741                                                              ace->flags,
04742                                                              ace->access_mask);
04743                                         }
04744                                 } else if (all_nt_acls) {
04745                                         if (determine_size) {
04746                                                 p = talloc_asprintf(
04747                                                         ctx, 
04748                                                         "%s%s:%d/%d/0x%08x",
04749                                                         i ? "," : "",
04750                                                         sidstr,
04751                                                         ace->type,
04752                                                         ace->flags,
04753                                                         ace->access_mask);
04754                                                 if (!p) {
04755                                                         errno = ENOMEM;
04756                                                         return -1;
04757                                                 }
04758                                                 n = strlen(p);
04759                                         } else {
04760                                                 n = snprintf(buf, bufsize,
04761                                                              "%s%s:%d/%d/0x%08x",
04762                                                              i ? "," : "",
04763                                                              sidstr,
04764                                                              ace->type,
04765                                                              ace->flags,
04766                                                              ace->access_mask);
04767                                         }
04768                                 }
04769                                 if (!determine_size && n > bufsize) {
04770                                         errno = ERANGE;
04771                                         return -1;
04772                                 }
04773                                 buf += n;
04774                                 n_used += n;
04775                                 bufsize -= n;
04776                                 n = 0;
04777                         }
04778                 }
04779 
04780                 /* Restore name pointer to its original value */
04781                 name -= 19;
04782         }
04783 
04784         if (all || some_dos) {
04785                 /* Point to the portion after "system.dos_attr." */
04786                 name += 16;     /* if (all) this will be invalid but unused */
04787 
04788                 /* Obtain the DOS attributes */
04789                 if (!smbc_getatr(context, srv, filename, &mode, &size, 
04790                                  &create_time_ts,
04791                                  &access_time_ts,
04792                                  &write_time_ts,
04793                                  &change_time_ts,
04794                                  &ino)) {
04795                         
04796                         errno = smbc_errno(context, srv->cli);
04797                         return -1;
04798                         
04799                 }
04800 
04801                 create_time = convert_timespec_to_time_t(create_time_ts);
04802                 access_time = convert_timespec_to_time_t(access_time_ts);
04803                 write_time = convert_timespec_to_time_t(write_time_ts);
04804                 change_time = convert_timespec_to_time_t(change_time_ts);
04805 
04806                 if (! exclude_dos_mode) {
04807                         if (all || all_dos) {
04808                                 if (determine_size) {
04809                                         p = talloc_asprintf(ctx,
04810                                                             "%sMODE:0x%x",
04811                                                             (ipc_cli &&
04812                                                              (all || some_nt)
04813                                                              ? ","
04814                                                              : ""),
04815                                                             mode);
04816                                         if (!p) {
04817                                                 errno = ENOMEM;
04818                                                 return -1;
04819                                         }
04820                                         n = strlen(p);
04821                                 } else {
04822                                         n = snprintf(buf, bufsize,
04823                                                      "%sMODE:0x%x",
04824                                                      (ipc_cli &&
04825                                                       (all || some_nt)
04826                                                       ? ","
04827                                                       : ""),
04828                                                      mode);
04829                                 }
04830                         } else if (StrCaseCmp(name, "mode") == 0) {
04831                                 if (determine_size) {
04832                                         p = talloc_asprintf(ctx, "0x%x", mode);
04833                                         if (!p) {
04834                                                 errno = ENOMEM;
04835                                                 return -1;
04836                                         }
04837                                         n = strlen(p);
04838                                 } else {
04839                                         n = snprintf(buf, bufsize,
04840                                                      "0x%x", mode);
04841                                 }
04842                         }
04843         
04844                         if (!determine_size && n > bufsize) {
04845                                 errno = ERANGE;
04846                                 return -1;
04847                         }
04848                         buf += n;
04849                         n_used += n;
04850                         bufsize -= n;
04851                         n = 0;
04852                 }
04853 
04854                 if (! exclude_dos_size) {
04855                         if (all || all_dos) {
04856                                 if (determine_size) {
04857                                         p = talloc_asprintf(
04858                                                 ctx,
04859                                                 ",SIZE:%.0f",
04860                                                 (double)size);
04861                                         if (!p) {
04862                                                 errno = ENOMEM;
04863                                                 return -1;
04864                                         }
04865                                         n = strlen(p);
04866                                 } else {
04867                                         n = snprintf(buf, bufsize,
04868                                                      ",SIZE:%.0f",
04869                                                      (double)size);
04870                                 }
04871                         } else if (StrCaseCmp(name, "size") == 0) {
04872                                 if (determine_size) {
04873                                         p = talloc_asprintf(
04874                                                 ctx,
04875                                                 "%.0f",
04876                                                 (double)size);
04877                                         if (!p) {
04878                                                 errno = ENOMEM;
04879                                                 return -1;
04880                                         }
04881                                         n = strlen(p);
04882                                 } else {
04883                                         n = snprintf(buf, bufsize,
04884                                                      "%.0f",
04885                                                      (double)size);
04886                                 }
04887                         }
04888         
04889                         if (!determine_size && n > bufsize) {
04890                                 errno = ERANGE;
04891                                 return -1;
04892                         }
04893                         buf += n;
04894                         n_used += n;
04895                         bufsize -= n;
04896                         n = 0;
04897                 }
04898 
04899                 if (! exclude_dos_create_time &&
04900                     attr_strings.create_time_attr != NULL) {
04901                         if (all || all_dos) {
04902                                 if (determine_size) {
04903                                         p = talloc_asprintf(ctx,
04904                                                             ",%s:%lu",
04905                                                             attr_strings.create_time_attr,
04906                                                             create_time);
04907                                         if (!p) {
04908                                                 errno = ENOMEM;
04909                                                 return -1;
04910                                         }
04911                                         n = strlen(p);
04912                                 } else {
04913                                         n = snprintf(buf, bufsize,
04914                                                      ",%s:%lu",
04915                                                      attr_strings.create_time_attr,
04916                                                      create_time);
04917                                 }
04918                         } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
04919                                 if (determine_size) {
04920                                         p = talloc_asprintf(ctx, "%lu", create_time);
04921                                         if (!p) {
04922                                                 errno = ENOMEM;
04923                                                 return -1;
04924                                         }
04925                                         n = strlen(p);
04926                                 } else {
04927                                         n = snprintf(buf, bufsize,
04928                                                      "%lu", create_time);
04929                                 }
04930                         }
04931         
04932                         if (!determine_size && n > bufsize) {
04933                                 errno = ERANGE;
04934                                 return -1;
04935                         }
04936                         buf += n;
04937                         n_used += n;
04938                         bufsize -= n;
04939                         n = 0;
04940                 }
04941 
04942                 if (! exclude_dos_access_time) {
04943                         if (all || all_dos) {
04944                                 if (determine_size) {
04945                                         p = talloc_asprintf(ctx,
04946                                                             ",%s:%lu",
04947                                                             attr_strings.access_time_attr,
04948                                                             access_time);
04949                                         if (!p) {
04950                                                 errno = ENOMEM;
04951                                                 return -1;
04952                                         }
04953                                         n = strlen(p);
04954                                 } else {
04955                                         n = snprintf(buf, bufsize,
04956                                                      ",%s:%lu",
04957                                                      attr_strings.access_time_attr,
04958                                                      access_time);
04959                                 }
04960                         } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
04961                                 if (determine_size) {
04962                                         p = talloc_asprintf(ctx, "%lu", access_time);
04963                                         if (!p) {
04964                                                 errno = ENOMEM;
04965                                                 return -1;
04966                                         }
04967                                         n = strlen(p);
04968                                 } else {
04969                                         n = snprintf(buf, bufsize,
04970                                                      "%lu", access_time);
04971                                 }
04972                         }
04973         
04974                         if (!determine_size && n > bufsize) {
04975                                 errno = ERANGE;
04976                                 return -1;
04977                         }
04978                         buf += n;
04979                         n_used += n;
04980                         bufsize -= n;
04981                         n = 0;
04982                 }
04983 
04984                 if (! exclude_dos_write_time) {
04985                         if (all || all_dos) {
04986                                 if (determine_size) {
04987                                         p = talloc_asprintf(ctx,
04988                                                             ",%s:%lu",
04989                                                             attr_strings.write_time_attr,
04990                                                             write_time);
04991                                         if (!p) {
04992                                                 errno = ENOMEM;
04993                                                 return -1;
04994                                         }
04995                                         n = strlen(p);
04996                                 } else {
04997                                         n = snprintf(buf, bufsize,
04998                                                      ",%s:%lu",
04999                                                      attr_strings.write_time_attr,
05000                                                      write_time);
05001                                 }
05002                         } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
05003                                 if (determine_size) {
05004                                         p = talloc_asprintf(ctx, "%lu", write_time);
05005                                         if (!p) {
05006                                                 errno = ENOMEM;
05007                                                 return -1;
05008                                         }
05009                                         n = strlen(p);
05010                                 } else {
05011                                         n = snprintf(buf, bufsize,
05012                                                      "%lu", write_time);
05013                                 }
05014                         }
05015         
05016                         if (!determine_size && n > bufsize) {
05017                                 errno = ERANGE;
05018                                 return -1;
05019                         }
05020                         buf += n;
05021                         n_used += n;
05022                         bufsize -= n;
05023                         n = 0;
05024                 }
05025 
05026                 if (! exclude_dos_change_time) {
05027                         if (all || all_dos) {
05028                                 if (determine_size) {
05029                                         p = talloc_asprintf(ctx,
05030                                                             ",%s:%lu",
05031                                                             attr_strings.change_time_attr,
05032                                                             change_time);
05033                                         if (!p) {
05034                                                 errno = ENOMEM;
05035                                                 return -1;
05036                                         }
05037                                         n = strlen(p);
05038                                 } else {
05039                                         n = snprintf(buf, bufsize,
05040                                                      ",%s:%lu",
05041                                                      attr_strings.change_time_attr,
05042                                                      change_time);
05043                                 }
05044                         } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
05045                                 if (determine_size) {
05046                                         p = talloc_asprintf(ctx, "%lu", change_time);
05047                                         if (!p) {
05048                                                 errno = ENOMEM;
05049                                                 return -1;
05050                                         }
05051                                         n = strlen(p);
05052                                 } else {
05053                                         n = snprintf(buf, bufsize,
05054                                                      "%lu", change_time);
05055                                 }
05056                         }
05057         
05058                         if (!determine_size && n > bufsize) {
05059                                 errno = ERANGE;
05060                                 return -1;
05061                         }
05062                         buf += n;
05063                         n_used += n;
05064                         bufsize -= n;
05065                         n = 0;
05066                 }
05067 
05068                 if (! exclude_dos_inode) {
05069                         if (all || all_dos) {
05070                                 if (determine_size) {
05071                                         p = talloc_asprintf(
05072                                                 ctx,
05073                                                 ",INODE:%.0f",
05074                                                 (double)ino);
05075                                         if (!p) {
05076                                                 errno = ENOMEM;
05077                                                 return -1;
05078                                         }
05079                                         n = strlen(p);
05080                                 } else {
05081                                         n = snprintf(buf, bufsize,
05082                                                      ",INODE:%.0f",
05083                                                      (double) ino);
05084                                 }
05085                         } else if (StrCaseCmp(name, "inode") == 0) {
05086                                 if (determine_size) {
05087                                         p = talloc_asprintf(
05088                                                 ctx,
05089                                                 "%.0f",
05090                                                 (double) ino);
05091                                         if (!p) {
05092                                                 errno = ENOMEM;
05093                                                 return -1;
05094                                         }
05095                                         n = strlen(p);
05096                                 } else {
05097                                         n = snprintf(buf, bufsize,
05098                                                      "%.0f",
05099                                                      (double) ino);
05100                                 }
05101                         }
05102         
05103                         if (!determine_size && n > bufsize) {
05104                                 errno = ERANGE;
05105                                 return -1;
05106                         }
05107                         buf += n;
05108                         n_used += n;
05109                         bufsize -= n;
05110                         n = 0;
05111                 }
05112 
05113                 /* Restore name pointer to its original value */
05114                 name -= 16;
05115         }
05116 
05117         if (n_used == 0) {
05118                 errno = ENOATTR;
05119                 return -1;
05120         }
05121 
05122         return n_used;
05123 }
05124 
05125 
05126 /***************************************************** 
05127 set the ACLs on a file given an ascii description
05128 *******************************************************/
05129 static int
05130 cacl_set(TALLOC_CTX *ctx,
05131          struct cli_state *cli,
05132          struct cli_state *ipc_cli,
05133          POLICY_HND *pol,
05134          const char *filename,
05135          const char *the_acl,
05136          int mode,
05137          int flags)
05138 {
05139         int fnum;
05140         int err = 0;
05141         SEC_DESC *sd = NULL, *old;
05142         SEC_ACL *dacl = NULL;
05143         DOM_SID *owner_sid = NULL; 
05144         DOM_SID *group_sid = NULL;
05145         uint32 i, j;
05146         size_t sd_size;
05147         int ret = 0;
05148         char *p;
05149         BOOL numeric = True;
05150 
05151         /* the_acl will be null for REMOVE_ALL operations */
05152         if (the_acl) {
05153                 numeric = ((p = strchr(the_acl, ':')) != NULL &&
05154                            p > the_acl &&
05155                            p[-1] != '+');
05156 
05157                 /* if this is to set the entire ACL... */
05158                 if (*the_acl == '*') {
05159                         /* ... then increment past the first colon */
05160                         the_acl = p + 1;
05161                 }
05162 
05163                 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
05164                                     CONST_DISCARD(char *, the_acl));
05165 
05166                 if (!sd) {
05167                         errno = EINVAL;
05168                         return -1;
05169                 }
05170         }
05171 
05172         /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
05173            that doesn't deref sd */
05174 
05175         if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
05176                 errno = EINVAL;
05177                 return -1;
05178         }
05179 
05180         /* The desired access below is the only one I could find that works
05181            with NT4, W2KP and Samba */
05182 
05183         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
05184 
05185         if (fnum == -1) {
05186                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
05187                           filename, cli_errstr(cli)));
05188                 errno = 0;
05189                 return -1;
05190         }
05191 
05192         old = cli_query_secdesc(cli, fnum, ctx);
05193 
05194         if (!old) {
05195                 DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
05196                 errno = 0;
05197                 return -1;
05198         }
05199 
05200         cli_close(cli, fnum);
05201 
05202         switch (mode) {
05203         case SMBC_XATTR_MODE_REMOVE_ALL:
05204                 old->dacl->num_aces = 0;
05205                 dacl = old->dacl;
05206                 break;
05207 
05208         case SMBC_XATTR_MODE_REMOVE:
05209                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
05210                         BOOL found = False;
05211 
05212                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
05213                                 if (sec_ace_equal(&sd->dacl->aces[i],
05214                                                   &old->dacl->aces[j])) {
05215                                         uint32 k;
05216                                         for (k=j; k<old->dacl->num_aces-1;k++) {
05217                                                 old->dacl->aces[k] =
05218                                                         old->dacl->aces[k+1];
05219                                         }
05220                                         old->dacl->num_aces--;
05221                                         found = True;
05222                                         dacl = old->dacl;
05223                                         break;
05224                                 }
05225                         }
05226 
05227                         if (!found) {
05228                                 err = ENOATTR;
05229                                 ret = -1;
05230                                 goto failed;
05231                         }
05232                 }
05233                 break;
05234 
05235         case SMBC_XATTR_MODE_ADD:
05236                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
05237                         BOOL found = False;
05238 
05239                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
05240                                 if (sid_equal(&sd->dacl->aces[i].trustee,
05241                                               &old->dacl->aces[j].trustee)) {
05242                                         if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
05243                                                 err = EEXIST;
05244                                                 ret = -1;
05245                                                 goto failed;
05246                                         }
05247                                         old->dacl->aces[j] = sd->dacl->aces[i];
05248                                         ret = -1;
05249                                         found = True;
05250                                 }
05251                         }
05252 
05253                         if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
05254                                 err = ENOATTR;
05255                                 ret = -1;
05256                                 goto failed;
05257                         }
05258                         
05259                         for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
05260                                 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
05261                         }
05262                 }
05263                 dacl = old->dacl;
05264                 break;
05265 
05266         case SMBC_XATTR_MODE_SET:
05267                 old = sd;
05268                 owner_sid = old->owner_sid;
05269                 group_sid = old->group_sid;
05270                 dacl = old->dacl;
05271                 break;
05272 
05273         case SMBC_XATTR_MODE_CHOWN:
05274                 owner_sid = sd->owner_sid;
05275                 break;
05276 
05277         case SMBC_XATTR_MODE_CHGRP:
05278                 group_sid = sd->group_sid;
05279                 break;
05280         }
05281 
05282         /* Denied ACE entries must come before allowed ones */
05283         sort_acl(old->dacl);
05284 
05285         /* Create new security descriptor and set it */
05286         sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, 
05287                            owner_sid, group_sid, NULL, dacl, &sd_size);
05288 
05289         fnum = cli_nt_create(cli, filename,
05290                              WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
05291 
05292         if (fnum == -1) {
05293                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
05294                           filename, cli_errstr(cli)));
05295                 errno = 0;
05296                 return -1;
05297         }
05298 
05299         if (!cli_set_secdesc(cli, fnum, sd)) {
05300                 DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
05301                 ret = -1;
05302         }
05303 
05304         /* Clean up */
05305 
05306  failed:
05307         cli_close(cli, fnum);
05308 
05309         if (err != 0) {
05310                 errno = err;
05311         }
05312         
05313         return ret;
05314 }
05315 
05316 
05317 static int
05318 smbc_setxattr_ctx(SMBCCTX *context,
05319                   const char *fname,
05320                   const char *name,
05321                   const void *value,
05322                   size_t size,
05323                   int flags)
05324 {
05325         int ret;
05326         int ret2;
05327         SMBCSRV *srv;
05328         SMBCSRV *ipc_srv;
05329         fstring server;
05330         fstring share;
05331         fstring user;
05332         fstring password;
05333         fstring workgroup;
05334         pstring path;
05335         TALLOC_CTX *ctx;
05336         POLICY_HND pol;
05337         DOS_ATTR_DESC *dad;
05338         struct {
05339                 const char * create_time_attr;
05340                 const char * access_time_attr;
05341                 const char * write_time_attr;
05342                 const char * change_time_attr;
05343         } attr_strings;
05344 
05345         if (!context || !context->internal ||
05346             !context->internal->_initialized) {
05347 
05348                 errno = EINVAL;  /* Best I can think of ... */
05349                 return -1;
05350     
05351         }
05352 
05353         if (!fname) {
05354 
05355                 errno = EINVAL;
05356                 return -1;
05357 
05358         }
05359   
05360         DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
05361                   fname, name, (int) size, (const char*)value));
05362 
05363         if (smbc_parse_path(context, fname,
05364                             workgroup, sizeof(workgroup),
05365                             server, sizeof(server),
05366                             share, sizeof(share),
05367                             path, sizeof(path),
05368                             user, sizeof(user),
05369                             password, sizeof(password),
05370                             NULL, 0)) {
05371                 errno = EINVAL;
05372                 return -1;
05373         }
05374 
05375         if (user[0] == (char)0) fstrcpy(user, context->user);
05376 
05377         srv = smbc_server(context, True,
05378                           server, share, workgroup, user, password);
05379         if (!srv) {
05380                 return -1;  /* errno set by smbc_server */
05381         }
05382 
05383         if (! srv->no_nt_session) {
05384                 ipc_srv = smbc_attr_server(context, server, share,
05385                                            workgroup, user, password,
05386                                            &pol);
05387                 if (! ipc_srv) {
05388                         srv->no_nt_session = True;
05389                 }
05390         } else {
05391                 ipc_srv = NULL;
05392         }
05393         
05394         ctx = talloc_init("smbc_setxattr");
05395         if (!ctx) {
05396                 errno = ENOMEM;
05397                 return -1;
05398         }
05399 
05400         /*
05401          * Are they asking to set the entire set of known attributes?
05402          */
05403         if (StrCaseCmp(name, "system.*") == 0 ||
05404             StrCaseCmp(name, "system.*+") == 0) {
05405                 /* Yup. */
05406                 char *namevalue =
05407                         talloc_asprintf(ctx, "%s:%s",
05408                                         name+7, (const char *) value);
05409                 if (! namevalue) {
05410                         errno = ENOMEM;
05411                         ret = -1;
05412                         return -1;
05413                 }
05414 
05415                 if (ipc_srv) {
05416                         ret = cacl_set(ctx, srv->cli,
05417                                        ipc_srv->cli, &pol, path,
05418                                        namevalue,
05419                                        (*namevalue == '*'
05420                                         ? SMBC_XATTR_MODE_SET
05421                                         : SMBC_XATTR_MODE_ADD),
05422                                        flags);
05423                 } else {
05424                         ret = 0;
05425                 }
05426 
05427                 /* get a DOS Attribute Descriptor with current attributes */
05428                 dad = dos_attr_query(context, ctx, path, srv);
05429                 if (dad) {
05430                         /* Overwrite old with new, using what was provided */
05431                         dos_attr_parse(context, dad, srv, namevalue);
05432 
05433                         /* Set the new DOS attributes */
05434                         if (! smbc_setatr(context, srv, path,
05435                                           dad->create_time,
05436                                           dad->access_time,
05437                                           dad->write_time,
05438                                           dad->change_time,
05439                                           dad->mode)) {
05440 
05441                                 /* cause failure if NT failed too */
05442                                 dad = NULL; 
05443                         }
05444                 }
05445 
05446                 /* we only fail if both NT and DOS sets failed */
05447                 if (ret < 0 && ! dad) {
05448                         ret = -1; /* in case dad was null */
05449                 }
05450                 else {
05451                         ret = 0;
05452                 }
05453 
05454                 talloc_destroy(ctx);
05455                 return ret;
05456         }
05457 
05458         /*
05459          * Are they asking to set an access control element or to set
05460          * the entire access control list?
05461          */
05462         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
05463             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
05464             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
05465             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
05466             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
05467 
05468                 /* Yup. */
05469                 char *namevalue =
05470                         talloc_asprintf(ctx, "%s:%s",
05471                                         name+19, (const char *) value);
05472 
05473                 if (! ipc_srv) {
05474                         ret = -1; /* errno set by smbc_server() */
05475                 }
05476                 else if (! namevalue) {
05477                         errno = ENOMEM;
05478                         ret = -1;
05479                 } else {
05480                         ret = cacl_set(ctx, srv->cli,
05481                                        ipc_srv->cli, &pol, path,
05482                                        namevalue,
05483                                        (*namevalue == '*'
05484                                         ? SMBC_XATTR_MODE_SET
05485                                         : SMBC_XATTR_MODE_ADD),
05486                                        flags);
05487                 }
05488                 talloc_destroy(ctx);
05489                 return ret;
05490         }
05491 
05492         /*
05493          * Are they asking to set the owner?
05494          */
05495         if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
05496             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
05497 
05498                 /* Yup. */
05499                 char *namevalue =
05500                         talloc_asprintf(ctx, "%s:%s",
05501                                         name+19, (const char *) value);
05502 
05503                 if (! ipc_srv) {
05504                         
05505                         ret = -1; /* errno set by smbc_server() */
05506                 }
05507                 else if (! namevalue) {
05508                         errno = ENOMEM;
05509                         ret = -1;
05510                 } else {
05511                         ret = cacl_set(ctx, srv->cli,
05512                                        ipc_srv->cli, &pol, path,
05513                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
05514                 }
05515                 talloc_destroy(ctx);
05516                 return ret;
05517         }
05518 
05519         /*
05520          * Are they asking to set the group?
05521          */
05522         if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
05523             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
05524 
05525                 /* Yup. */
05526                 char *namevalue =
05527                         talloc_asprintf(ctx, "%s:%s",
05528                                         name+19, (const char *) value);
05529 
05530                 if (! ipc_srv) {
05531                         /* errno set by smbc_server() */
05532                         ret = -1;
05533                 }
05534                 else if (! namevalue) {
05535                         errno = ENOMEM;
05536                         ret = -1;
05537                 } else {
05538                         ret = cacl_set(ctx, srv->cli,
05539                                        ipc_srv->cli, &pol, path,
05540                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
05541                 }
05542                 talloc_destroy(ctx);
05543                 return ret;
05544         }
05545 
05546         /* Determine whether to use old-style or new-style attribute names */
05547         if (context->internal->_full_time_names) {
05548                 /* new-style names */
05549                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
05550                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
05551                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
05552                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
05553         } else {
05554                 /* old-style names */
05555                 attr_strings.create_time_attr = NULL;
05556                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
05557                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
05558                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
05559         }
05560 
05561         /*
05562          * Are they asking to set a DOS attribute?
05563          */
05564         if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
05565             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
05566             (attr_strings.create_time_attr != NULL &&
05567              StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
05568             StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
05569             StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
05570             StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
05571 
05572                 /* get a DOS Attribute Descriptor with current attributes */
05573                 dad = dos_attr_query(context, ctx, path, srv);
05574                 if (dad) {
05575                         char *namevalue =
05576                                 talloc_asprintf(ctx, "%s:%s",
05577                                                 name+16, (const char *) value);
05578                         if (! namevalue) {
05579                                 errno = ENOMEM;
05580                                 ret = -1;
05581                         } else {
05582                                 /* Overwrite old with provided new params */
05583                                 dos_attr_parse(context, dad, srv, namevalue);
05584 
05585                                 /* Set the new DOS attributes */
05586                                 ret2 = smbc_setatr(context, srv, path,
05587                                                    dad->create_time,
05588                                                    dad->access_time,
05589                                                    dad->write_time,
05590                                                    dad->change_time,
05591                                                    dad->mode);
05592 
05593                                 /* ret2 has True (success) / False (failure) */
05594                                 if (ret2) {
05595                                         ret = 0;
05596                                 } else {
05597                                         ret = -1;
05598                                 }
05599                         }
05600                 } else {
05601                         ret = -1;
05602                 }
05603 
05604                 talloc_destroy(ctx);
05605                 return ret;
05606         }
05607 
05608         /* Unsupported attribute name */
05609         talloc_destroy(ctx);
05610         errno = EINVAL;
05611         return -1;
05612 }
05613 
05614 static int
05615 smbc_getxattr_ctx(SMBCCTX *context,
05616                   const char *fname,
05617                   const char *name,
05618                   const void *value,
05619                   size_t size)
05620 {
05621         int ret;
05622         SMBCSRV *srv;
05623         SMBCSRV *ipc_srv;
05624         fstring server;
05625         fstring share;
05626         fstring user;
05627         fstring password;
05628         fstring workgroup;
05629         pstring path;
05630         TALLOC_CTX *ctx;
05631         POLICY_HND pol;
05632         struct {
05633                 const char * create_time_attr;
05634                 const char * access_time_attr;
05635                 const char * write_time_attr;
05636                 const char * change_time_attr;
05637         } attr_strings;
05638 
05639 
05640         if (!context || !context->internal ||
05641             !context->internal->_initialized) {
05642 
05643                 errno = EINVAL;  /* Best I can think of ... */
05644                 return -1;
05645     
05646         }
05647 
05648         if (!fname) {
05649 
05650                 errno = EINVAL;
05651                 return -1;
05652 
05653         }
05654   
05655         DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
05656 
05657         if (smbc_parse_path(context, fname,
05658                             workgroup, sizeof(workgroup),
05659                             server, sizeof(server),
05660                             share, sizeof(share),
05661                             path, sizeof(path),
05662                             user, sizeof(user),
05663                             password, sizeof(password),
05664                             NULL, 0)) {
05665                 errno = EINVAL;
05666                 return -1;
05667         }
05668 
05669         if (user[0] == (char)0) fstrcpy(user, context->user);
05670 
05671         srv = smbc_server(context, True,
05672                           server, share, workgroup, user, password);
05673         if (!srv) {
05674                 return -1;  /* errno set by smbc_server */
05675         }
05676 
05677         if (! srv->no_nt_session) {
05678                 ipc_srv = smbc_attr_server(context, server, share,
05679                                            workgroup, user, password,
05680                                            &pol);
05681                 if (! ipc_srv) {
05682                         srv->no_nt_session = True;
05683                 }
05684         } else {
05685                 ipc_srv = NULL;
05686         }
05687         
05688         ctx = talloc_init("smbc:getxattr");
05689         if (!ctx) {
05690                 errno = ENOMEM;
05691                 return -1;
05692         }
05693 
05694         /* Determine whether to use old-style or new-style attribute names */
05695         if (context->internal->_full_time_names) {
05696                 /* new-style names */
05697                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
05698                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
05699                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
05700                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
05701         } else {
05702                 /* old-style names */
05703                 attr_strings.create_time_attr = NULL;
05704                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
05705                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
05706                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
05707         }
05708 
05709         /* Are they requesting a supported attribute? */
05710         if (StrCaseCmp(name, "system.*") == 0 ||
05711             StrnCaseCmp(name, "system.*!", 9) == 0 ||
05712             StrCaseCmp(name, "system.*+") == 0 ||
05713             StrnCaseCmp(name, "system.*+!", 10) == 0 ||
05714             StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
05715             StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
05716             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
05717             StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
05718             StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
05719             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
05720             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
05721             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
05722             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
05723             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
05724             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
05725             StrCaseCmp(name, "system.dos_attr.*") == 0 ||
05726             StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
05727             StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
05728             StrCaseCmp(name, "system.dos_attr.size") == 0 ||
05729             (attr_strings.create_time_attr != NULL &&
05730              StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
05731             StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
05732             StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
05733             StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
05734             StrCaseCmp(name, "system.dos_attr.inode") == 0) {
05735 
05736                 /* Yup. */
05737                 ret = cacl_get(context, ctx, srv,
05738                                ipc_srv == NULL ? NULL : ipc_srv->cli, 
05739                                &pol, path,
05740                                CONST_DISCARD(char *, name),
05741                                CONST_DISCARD(char *, value), size);
05742                 if (ret < 0 && errno == 0) {
05743                         errno = smbc_errno(context, srv->cli);
05744                 }
05745                 talloc_destroy(ctx);
05746                 return ret;
05747         }
05748 
05749         /* Unsupported attribute name */
05750         talloc_destroy(ctx);
05751         errno = EINVAL;
05752         return -1;
05753 }
05754 
05755 
05756 static int
05757 smbc_removexattr_ctx(SMBCCTX *context,
05758                      const char *fname,
05759                      const char *name)
05760 {
05761         int ret;
05762         SMBCSRV *srv;
05763         SMBCSRV *ipc_srv;
05764         fstring server;
05765         fstring share;
05766         fstring user;
05767         fstring password;
05768         fstring workgroup;
05769         pstring path;
05770         TALLOC_CTX *ctx;
05771         POLICY_HND pol;
05772 
05773         if (!context || !context->internal ||
05774             !context->internal->_initialized) {
05775 
05776                 errno = EINVAL;  /* Best I can think of ... */
05777                 return -1;
05778     
05779         }
05780 
05781         if (!fname) {
05782 
05783                 errno = EINVAL;
05784                 return -1;
05785 
05786         }
05787   
05788         DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
05789 
05790         if (smbc_parse_path(context, fname,
05791                             workgroup, sizeof(workgroup),
05792                             server, sizeof(server),
05793                             share, sizeof(share),
05794                             path, sizeof(path),
05795                             user, sizeof(user),
05796                             password, sizeof(password),
05797                             NULL, 0)) {
05798                 errno = EINVAL;
05799                 return -1;
05800         }
05801 
05802         if (user[0] == (char)0) fstrcpy(user, context->user);
05803 
05804         srv = smbc_server(context, True,
05805                           server, share, workgroup, user, password);
05806         if (!srv) {
05807                 return -1;  /* errno set by smbc_server */
05808         }
05809 
05810         if (! srv->no_nt_session) {
05811                 ipc_srv = smbc_attr_server(context, server, share,
05812                                            workgroup, user, password,
05813                                            &pol);
05814                 if (! ipc_srv) {
05815                         srv->no_nt_session = True;
05816                 }
05817         } else {
05818                 ipc_srv = NULL;
05819         }
05820         
05821         if (! ipc_srv) {
05822                 return -1; /* errno set by smbc_attr_server */
05823         }
05824 
05825         ctx = talloc_init("smbc_removexattr");
05826         if (!ctx) {
05827                 errno = ENOMEM;
05828                 return -1;
05829         }
05830 
05831         /* Are they asking to set the entire ACL? */
05832         if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
05833             StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
05834 
05835                 /* Yup. */
05836                 ret = cacl_set(ctx, srv->cli,
05837                                ipc_srv->cli, &pol, path,
05838                                NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
05839                 talloc_destroy(ctx);
05840                 return ret;
05841         }
05842 
05843         /*
05844          * Are they asking to remove one or more spceific security descriptor
05845          * attributes?
05846          */
05847         if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
05848             StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
05849             StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
05850             StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
05851             StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
05852             StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
05853             StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
05854 
05855                 /* Yup. */
05856                 ret = cacl_set(ctx, srv->cli,
05857                                ipc_srv->cli, &pol, path,
05858                                name + 19, SMBC_XATTR_MODE_REMOVE, 0);
05859                 talloc_destroy(ctx);
05860                 return ret;
05861         }
05862 
05863         /* Unsupported attribute name */
05864         talloc_destroy(ctx);
05865         errno = EINVAL;
05866         return -1;
05867 }
05868 
05869 static int
05870 smbc_listxattr_ctx(SMBCCTX *context,
05871                    const char *fname,
05872                    char *list,
05873                    size_t size)
05874 {
05875         /*
05876          * This isn't quite what listxattr() is supposed to do.  This returns
05877          * the complete set of attribute names, always, rather than only those
05878          * attribute names which actually exist for a file.  Hmmm...
05879          */
05880         const char supported_old[] =
05881                 "system.*\0"
05882                 "system.*+\0"
05883                 "system.nt_sec_desc.revision\0"
05884                 "system.nt_sec_desc.owner\0"
05885                 "system.nt_sec_desc.owner+\0"
05886                 "system.nt_sec_desc.group\0"
05887                 "system.nt_sec_desc.group+\0"
05888                 "system.nt_sec_desc.acl.*\0"
05889                 "system.nt_sec_desc.acl\0"
05890                 "system.nt_sec_desc.acl+\0"
05891                 "system.nt_sec_desc.*\0"
05892                 "system.nt_sec_desc.*+\0"
05893                 "system.dos_attr.*\0"
05894                 "system.dos_attr.mode\0"
05895                 "system.dos_attr.c_time\0"
05896                 "system.dos_attr.a_time\0"
05897                 "system.dos_attr.m_time\0"
05898                 ;
05899         const char supported_new[] =
05900                 "system.*\0"
05901                 "system.*+\0"
05902                 "system.nt_sec_desc.revision\0"
05903                 "system.nt_sec_desc.owner\0"
05904                 "system.nt_sec_desc.owner+\0"
05905                 "system.nt_sec_desc.group\0"
05906                 "system.nt_sec_desc.group+\0"
05907                 "system.nt_sec_desc.acl.*\0"
05908                 "system.nt_sec_desc.acl\0"
05909                 "system.nt_sec_desc.acl+\0"
05910                 "system.nt_sec_desc.*\0"
05911                 "system.nt_sec_desc.*+\0"
05912                 "system.dos_attr.*\0"
05913                 "system.dos_attr.mode\0"
05914                 "system.dos_attr.create_time\0"
05915                 "system.dos_attr.access_time\0"
05916                 "system.dos_attr.write_time\0"
05917                 "system.dos_attr.change_time\0"
05918                 ;
05919         const char * supported;
05920 
05921         if (context->internal->_full_time_names) {
05922                 supported = supported_new;
05923         } else {
05924                 supported = supported_old;
05925         }
05926 
05927         if (size == 0) {
05928                 return sizeof(supported);
05929         }
05930 
05931         if (sizeof(supported) > size) {
05932                 errno = ERANGE;
05933                 return -1;
05934         }
05935 
05936         /* this can't be strcpy() because there are embedded null characters */
05937         memcpy(list, supported, sizeof(supported));
05938         return sizeof(supported);
05939 }
05940 
05941 
05942 /*
05943  * Open a print file to be written to by other calls
05944  */
05945 
05946 static SMBCFILE *
05947 smbc_open_print_job_ctx(SMBCCTX *context,
05948                         const char *fname)
05949 {
05950         fstring server;
05951         fstring share;
05952         fstring user;
05953         fstring password;
05954         pstring path;
05955         
05956         if (!context || !context->internal ||
05957             !context->internal->_initialized) {
05958 
05959                 errno = EINVAL;
05960                 return NULL;
05961     
05962         }
05963 
05964         if (!fname) {
05965 
05966                 errno = EINVAL;
05967                 return NULL;
05968 
05969         }
05970   
05971         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
05972 
05973         if (smbc_parse_path(context, fname,
05974                             NULL, 0,
05975                             server, sizeof(server),
05976                             share, sizeof(share),
05977                             path, sizeof(path),
05978                             user, sizeof(user),
05979                             password, sizeof(password),
05980                             NULL, 0)) {
05981                 errno = EINVAL;
05982                 return NULL;
05983         }
05984 
05985         /* What if the path is empty, or the file exists? */
05986 
05987         return (context->open)(context, fname, O_WRONLY, 666);
05988 
05989 }
05990 
05991 /*
05992  * Routine to print a file on a remote server ...
05993  *
05994  * We open the file, which we assume to be on a remote server, and then
05995  * copy it to a print file on the share specified by printq.
05996  */
05997 
05998 static int
05999 smbc_print_file_ctx(SMBCCTX *c_file,
06000                     const char *fname,
06001                     SMBCCTX *c_print,
06002                     const char *printq)
06003 {
06004         SMBCFILE *fid1;
06005         SMBCFILE *fid2;
06006         int bytes;
06007         int saverr;
06008         int tot_bytes = 0;
06009         char buf[4096];
06010 
06011         if (!c_file || !c_file->internal->_initialized || !c_print ||
06012             !c_print->internal->_initialized) {
06013 
06014                 errno = EINVAL;
06015                 return -1;
06016 
06017         }
06018 
06019         if (!fname && !printq) {
06020 
06021                 errno = EINVAL;
06022                 return -1;
06023 
06024         }
06025 
06026         /* Try to open the file for reading ... */
06027 
06028         if ((long)(fid1 = (c_file->open)(c_file, fname, O_RDONLY, 0666)) < 0) {
06029                 
06030                 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
06031                 return -1;  /* smbc_open sets errno */
06032                 
06033         }
06034 
06035         /* Now, try to open the printer file for writing */
06036 
06037         if ((long)(fid2 = (c_print->open_print_job)(c_print, printq)) < 0) {
06038 
06039                 saverr = errno;  /* Save errno */
06040                 (c_file->close_fn)(c_file, fid1);
06041                 errno = saverr;
06042                 return -1;
06043 
06044         }
06045 
06046         while ((bytes = (c_file->read)(c_file, fid1, buf, sizeof(buf))) > 0) {
06047 
06048                 tot_bytes += bytes;
06049 
06050                 if (((c_print->write)(c_print, fid2, buf, bytes)) < 0) {
06051 
06052                         saverr = errno;
06053                         (c_file->close_fn)(c_file, fid1);
06054                         (c_print->close_fn)(c_print, fid2);
06055                         errno = saverr;
06056 
06057                 }
06058 
06059         }
06060 
06061         saverr = errno;
06062 
06063         (c_file->close_fn)(c_file, fid1);  /* We have to close these anyway */
06064         (c_print->close_fn)(c_print, fid2);
06065 
06066         if (bytes < 0) {
06067 
06068                 errno = saverr;
06069                 return -1;
06070 
06071         }
06072 
06073         return tot_bytes;
06074 
06075 }
06076 
06077 /*
06078  * Routine to list print jobs on a printer share ...
06079  */
06080 
06081 static int
06082 smbc_list_print_jobs_ctx(SMBCCTX *context,
06083                          const char *fname,
06084                          smbc_list_print_job_fn fn)
06085 {
06086         SMBCSRV *srv;
06087         fstring server;
06088         fstring share;
06089         fstring user;
06090         fstring password;
06091         fstring workgroup;
06092         pstring path;
06093 
06094         if (!context || !context->internal ||
06095             !context->internal->_initialized) {
06096 
06097                 errno = EINVAL;
06098                 return -1;
06099 
06100         }
06101 
06102         if (!fname) {
06103                 
06104                 errno = EINVAL;
06105                 return -1;
06106 
06107         }
06108   
06109         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
06110 
06111         if (smbc_parse_path(context, fname,
06112                             workgroup, sizeof(workgroup),
06113                             server, sizeof(server),
06114                             share, sizeof(share),
06115                             path, sizeof(path),
06116                             user, sizeof(user),
06117                             password, sizeof(password),
06118                             NULL, 0)) {
06119                 errno = EINVAL;
06120                 return -1;
06121         }
06122 
06123         if (user[0] == (char)0) fstrcpy(user, context->user);
06124         
06125         srv = smbc_server(context, True,
06126                           server, share, workgroup, user, password);
06127 
06128         if (!srv) {
06129 
06130                 return -1;  /* errno set by smbc_server */
06131 
06132         }
06133 
06134         if (cli_print_queue(srv->cli,
06135                             (void (*)(struct print_job_info *))fn) < 0) {
06136 
06137                 errno = smbc_errno(context, srv->cli);
06138                 return -1;
06139 
06140         }
06141         
06142         return 0;
06143 
06144 }
06145 
06146 /*
06147  * Delete a print job from a remote printer share
06148  */
06149 
06150 static int
06151 smbc_unlink_print_job_ctx(SMBCCTX *context,
06152                           const char *fname,
06153                           int id)
06154 {
06155         SMBCSRV *srv;
06156         fstring server;
06157         fstring share;
06158         fstring user;
06159         fstring password;
06160         fstring workgroup;
06161         pstring path;
06162         int err;
06163 
06164         if (!context || !context->internal ||
06165             !context->internal->_initialized) {
06166 
06167                 errno = EINVAL;
06168                 return -1;
06169 
06170         }
06171 
06172         if (!fname) {
06173 
06174                 errno = EINVAL;
06175                 return -1;
06176 
06177         }
06178   
06179         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
06180 
06181         if (smbc_parse_path(context, fname,
06182                             workgroup, sizeof(workgroup),
06183                             server, sizeof(server),
06184                             share, sizeof(share),
06185                             path, sizeof(path),
06186                             user, sizeof(user),
06187                             password, sizeof(password),
06188                             NULL, 0)) {
06189                 errno = EINVAL;
06190                 return -1;
06191         }
06192 
06193         if (user[0] == (char)0) fstrcpy(user, context->user);
06194 
06195         srv = smbc_server(context, True,
06196                           server, share, workgroup, user, password);
06197 
06198         if (!srv) {
06199 
06200                 return -1;  /* errno set by smbc_server */
06201 
06202         }
06203 
06204         if ((err = cli_printjob_del(srv->cli, id)) != 0) {
06205 
06206                 if (err < 0)
06207                         errno = smbc_errno(context, srv->cli);
06208                 else if (err == ERRnosuchprintjob)
06209                         errno = EINVAL;
06210                 return -1;
06211 
06212         }
06213 
06214         return 0;
06215 
06216 }
06217 
06218 /*
06219  * Get a new empty handle to fill in with your own info 
06220  */
06221 SMBCCTX *
06222 smbc_new_context(void)
06223 {
06224         SMBCCTX *context;
06225 
06226         context = SMB_MALLOC_P(SMBCCTX);
06227         if (!context) {
06228                 errno = ENOMEM;
06229                 return NULL;
06230         }
06231 
06232         ZERO_STRUCTP(context);
06233 
06234         context->internal = SMB_MALLOC_P(struct smbc_internal_data);
06235         if (!context->internal) {
06236                 SAFE_FREE(context);
06237                 errno = ENOMEM;
06238                 return NULL;
06239         }
06240 
06241         ZERO_STRUCTP(context->internal);
06242 
06243         
06244         /* ADD REASONABLE DEFAULTS */
06245         context->debug            = 0;
06246         context->timeout          = 20000; /* 20 seconds */
06247 
06248         context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
06249         context->options.urlencode_readdir_entries = False;/* backward compat */
06250         context->options.one_share_per_server      = False;/* backward compat */
06251         context->internal->_share_mode             = SMBC_SHAREMODE_DENY_NONE;
06252                                 /* backward compat */
06253 
06254         context->open                              = smbc_open_ctx;
06255         context->creat                             = smbc_creat_ctx;
06256         context->read                              = smbc_read_ctx;
06257         context->write                             = smbc_write_ctx;
06258         context->close_fn                          = smbc_close_ctx;
06259         context->unlink                            = smbc_unlink_ctx;
06260         context->rename                            = smbc_rename_ctx;
06261         context->lseek                             = smbc_lseek_ctx;
06262         context->stat                              = smbc_stat_ctx;
06263         context->fstat                             = smbc_fstat_ctx;
06264         context->opendir                           = smbc_opendir_ctx;
06265         context->closedir                          = smbc_closedir_ctx;
06266         context->readdir                           = smbc_readdir_ctx;
06267         context->getdents                          = smbc_getdents_ctx;
06268         context->mkdir                             = smbc_mkdir_ctx;
06269         context->rmdir                             = smbc_rmdir_ctx;
06270         context->telldir                           = smbc_telldir_ctx;
06271         context->lseekdir                          = smbc_lseekdir_ctx;
06272         context->fstatdir                          = smbc_fstatdir_ctx;
06273         context->chmod                             = smbc_chmod_ctx;
06274         context->utimes                            = smbc_utimes_ctx;
06275         context->setxattr                          = smbc_setxattr_ctx;
06276         context->getxattr                          = smbc_getxattr_ctx;
06277         context->removexattr                       = smbc_removexattr_ctx;
06278         context->listxattr                         = smbc_listxattr_ctx;
06279         context->open_print_job                    = smbc_open_print_job_ctx;
06280         context->print_file                        = smbc_print_file_ctx;
06281         context->list_print_jobs                   = smbc_list_print_jobs_ctx;
06282         context->unlink_print_job                  = smbc_unlink_print_job_ctx;
06283 
06284         context->callbacks.check_server_fn         = smbc_check_server;
06285         context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
06286 
06287         smbc_default_cache_functions(context);
06288 
06289         return context;
06290 }
06291 
06292 /* 
06293  * Free a context
06294  *
06295  * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed 
06296  * and thus you'll be leaking memory if not handled properly.
06297  *
06298  */
06299 int
06300 smbc_free_context(SMBCCTX *context,
06301                   int shutdown_ctx)
06302 {
06303         if (!context) {
06304                 errno = EBADF;
06305                 return 1;
06306         }
06307         
06308         if (shutdown_ctx) {
06309                 SMBCFILE * f;
06310                 DEBUG(1,("Performing aggressive shutdown.\n"));
06311                 
06312                 f = context->internal->_files;
06313                 while (f) {
06314                         (context->close_fn)(context, f);
06315                         f = f->next;
06316                 }
06317                 context->internal->_files = NULL;
06318 
06319                 /* First try to remove the servers the nice way. */
06320                 if (context->callbacks.purge_cached_fn(context)) {
06321                         SMBCSRV * s;
06322                         SMBCSRV * next;
06323                         DEBUG(1, ("Could not purge all servers, "
06324                                   "Nice way shutdown failed.\n"));
06325                         s = context->internal->_servers;
06326                         while (s) {
06327                                 DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
06328                                           s, s->cli->fd));
06329                                 cli_shutdown(s->cli);
06330                                 (context->callbacks.remove_cached_srv_fn)(context,
06331                                                                           s);
06332                                 next = s->next;
06333                                 DLIST_REMOVE(context->internal->_servers, s);
06334                                 SAFE_FREE(s);
06335                                 s = next;
06336                         }
06337                         context->internal->_servers = NULL;
06338                 }
06339         }
06340         else {
06341                 /* This is the polite way */    
06342                 if ((context->callbacks.purge_cached_fn)(context)) {
06343                         DEBUG(1, ("Could not purge all servers, "
06344                                   "free_context failed.\n"));
06345                         errno = EBUSY;
06346                         return 1;
06347                 }
06348                 if (context->internal->_servers) {
06349                         DEBUG(1, ("Active servers in context, "
06350                                   "free_context failed.\n"));
06351                         errno = EBUSY;
06352                         return 1;
06353                 }
06354                 if (context->internal->_files) {
06355                         DEBUG(1, ("Active files in context, "
06356                                   "free_context failed.\n"));
06357                         errno = EBUSY;
06358                         return 1;
06359                 }               
06360         }
06361 
06362         /* Things we have to clean up */
06363         SAFE_FREE(context->workgroup);
06364         SAFE_FREE(context->netbios_name);
06365         SAFE_FREE(context->user);
06366         
06367         DEBUG(3, ("Context %p successfully freed\n", context));
06368         SAFE_FREE(context->internal);
06369         SAFE_FREE(context);
06370         return 0;
06371 }
06372 
06373 
06374 /*
06375  * Each time the context structure is changed, we have binary backward
06376  * compatibility issues.  Instead of modifying the public portions of the
06377  * context structure to add new options, instead, we put them in the internal
06378  * portion of the context structure and provide a set function for these new
06379  * options.
06380  */
06381 void
06382 smbc_option_set(SMBCCTX *context,
06383                 char *option_name,
06384                 ... /* option_value */)
06385 {
06386         va_list ap;
06387         union {
06388                 int i;
06389                 BOOL b;
06390                 smbc_get_auth_data_with_context_fn auth_fn;
06391                 void *v;
06392         } option_value;
06393 
06394         va_start(ap, option_name);
06395 
06396         if (strcmp(option_name, "debug_to_stderr") == 0) {
06397                 /*
06398                  * Log to standard error instead of standard output.
06399                  */
06400                 option_value.b = (BOOL) va_arg(ap, int);
06401                 context->internal->_debug_stderr = option_value.b;
06402 
06403         } else if (strcmp(option_name, "full_time_names") == 0) {
06404                 /*
06405                  * Use new-style time attribute names, e.g. WRITE_TIME rather
06406                  * than the old-style names such as M_TIME.  This allows also
06407                  * setting/getting CREATE_TIME which was previously
06408                  * unimplemented.  (Note that the old C_TIME was supposed to
06409                  * be CHANGE_TIME but was confused and sometimes referred to
06410                  * CREATE_TIME.)
06411                  */
06412                 option_value.b = (BOOL) va_arg(ap, int);
06413                 context->internal->_full_time_names = option_value.b;
06414 
06415         } else if (strcmp(option_name, "open_share_mode") == 0) {
06416                 /*
06417                  * The share mode to use for files opened with
06418                  * smbc_open_ctx().  The default is SMBC_SHAREMODE_DENY_NONE.
06419                  */
06420                 option_value.i = va_arg(ap, int);
06421                 context->internal->_share_mode =
06422                         (smbc_share_mode) option_value.i;
06423 
06424         } else if (strcmp(option_name, "auth_function") == 0) {
06425                 /*
06426                  * Use the new-style authentication function which includes
06427                  * the context.
06428                  */
06429                 option_value.auth_fn =
06430                         va_arg(ap, smbc_get_auth_data_with_context_fn);
06431                 context->internal->_auth_fn_with_context =
06432                         option_value.auth_fn;
06433         } else if (strcmp(option_name, "user_data") == 0) {
06434                 /*
06435                  * Save a user data handle which may be retrieved by the user
06436                  * with smbc_option_get()
06437                  */
06438                 option_value.v = va_arg(ap, void *);
06439                 context->internal->_user_data = option_value.v;
06440         }
06441 
06442         va_end(ap);
06443 }
06444 
06445 
06446 /*
06447  * Retrieve the current value of an option
06448  */
06449 void *
06450 smbc_option_get(SMBCCTX *context,
06451                 char *option_name)
06452 {
06453         if (strcmp(option_name, "debug_stderr") == 0) {
06454                 /*
06455                  * Log to standard error instead of standard output.
06456                  */
06457 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
06458                 return (void *) (intptr_t) context->internal->_debug_stderr;
06459 #else
06460                 return (void *) context->internal->_debug_stderr;
06461 #endif
06462         } else if (strcmp(option_name, "full_time_names") == 0) {
06463                 /*
06464                  * Use new-style time attribute names, e.g. WRITE_TIME rather
06465                  * than the old-style names such as M_TIME.  This allows also
06466                  * setting/getting CREATE_TIME which was previously
06467                  * unimplemented.  (Note that the old C_TIME was supposed to
06468                  * be CHANGE_TIME but was confused and sometimes referred to
06469                  * CREATE_TIME.)
06470                  */
06471 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
06472                 return (void *) (intptr_t) context->internal->_full_time_names;
06473 #else
06474                 return (void *) context->internal->_full_time_names;
06475 #endif
06476 
06477         } else if (strcmp(option_name, "auth_function") == 0) {
06478                 /*
06479                  * Use the new-style authentication function which includes
06480                  * the context.
06481                  */
06482                 return (void *) context->internal->_auth_fn_with_context;
06483         } else if (strcmp(option_name, "user_data") == 0) {
06484                 /*
06485                  * Save a user data handle which may be retrieved by the user
06486                  * with smbc_option_get()
06487                  */
06488                 return context->internal->_user_data;
06489         }
06490 
06491         return NULL;
06492 }
06493 
06494 
06495 /*
06496  * Initialise the library etc 
06497  *
06498  * We accept a struct containing handle information.
06499  * valid values for info->debug from 0 to 100,
06500  * and insist that info->fn must be non-null.
06501  */
06502 SMBCCTX *
06503 smbc_init_context(SMBCCTX *context)
06504 {
06505         pstring conf;
06506         int pid;
06507         char *user = NULL;
06508         char *home = NULL;
06509 
06510         if (!context || !context->internal) {
06511                 errno = EBADF;
06512                 return NULL;
06513         }
06514 
06515         /* Do not initialise the same client twice */
06516         if (context->internal->_initialized) { 
06517                 return 0;
06518         }
06519 
06520         if ((!context->callbacks.auth_fn &&
06521              !context->internal->_auth_fn_with_context) ||
06522             context->debug < 0 ||
06523             context->debug > 100) {
06524 
06525                 errno = EINVAL;
06526                 return NULL;
06527 
06528         }
06529 
06530         if (!smbc_initialized) {
06531                 /*
06532                  * Do some library-wide intializations the first time we get
06533                  * called
06534                  */
06535                 BOOL conf_loaded = False;
06536 
06537                 /* Set this to what the user wants */
06538                 DEBUGLEVEL = context->debug;
06539                 
06540                 load_case_tables();
06541 
06542                 setup_logging("libsmbclient", True);
06543                 if (context->internal->_debug_stderr) {
06544                         dbf = x_stderr;
06545                         x_setbuf(x_stderr, NULL);
06546                 }
06547 
06548                 /* Here we would open the smb.conf file if needed ... */
06549                 
06550                 in_client = True; /* FIXME, make a param */
06551 
06552                 home = getenv("HOME");
06553                 if (home) {
06554                         slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
06555                         if (lp_load(conf, True, False, False, True)) {
06556                                 conf_loaded = True;
06557                         } else {
06558                                 DEBUG(5, ("Could not load config file: %s\n",
06559                                           conf));
06560                         }
06561                 }
06562  
06563                 if (!conf_loaded) {
06564                         /*
06565                          * Well, if that failed, try the dyn_CONFIGFILE
06566                          * Which points to the standard locn, and if that
06567                          * fails, silently ignore it and use the internal
06568                          * defaults ...
06569                          */
06570 
06571                         if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
06572                                 DEBUG(5, ("Could not load config file: %s\n",
06573                                           dyn_CONFIGFILE));
06574                         } else if (home) {
06575                                 /*
06576                                  * We loaded the global config file.  Now lets
06577                                  * load user-specific modifications to the
06578                                  * global config.
06579                                  */
06580                                 slprintf(conf, sizeof(conf),
06581                                          "%s/.smb/smb.conf.append", home);
06582                                 if (!lp_load(conf, True, False, False, False)) {
06583                                         DEBUG(10,
06584                                               ("Could not append config file: "
06585                                                "%s\n",
06586                                                conf));
06587                                 }
06588                         }
06589                 }
06590 
06591                 load_interfaces();  /* Load the list of interfaces ... */
06592                 
06593                 reopen_logs();  /* Get logging working ... */
06594         
06595                 /* 
06596                  * Block SIGPIPE (from lib/util_sock.c: write())  
06597                  * It is not needed and should not stop execution 
06598                  */
06599                 BlockSignals(True, SIGPIPE);
06600                 
06601                 /* Done with one-time initialisation */
06602                 smbc_initialized = 1; 
06603 
06604         }
06605         
06606         if (!context->user) {
06607                 /*
06608                  * FIXME: Is this the best way to get the user info? 
06609                  */
06610                 user = getenv("USER");
06611                 /* walk around as "guest" if no username can be found */
06612                 if (!user) context->user = SMB_STRDUP("guest");
06613                 else context->user = SMB_STRDUP(user);
06614         }
06615 
06616         if (!context->netbios_name) {
06617                 /*
06618                  * We try to get our netbios name from the config. If that
06619                  * fails we fall back on constructing our netbios name from
06620                  * our hostname etc
06621                  */
06622                 if (global_myname()) {
06623                         context->netbios_name = SMB_STRDUP(global_myname());
06624                 }
06625                 else {
06626                         /*
06627                          * Hmmm, I want to get hostname as well, but I am too
06628                          * lazy for the moment
06629                          */
06630                         pid = sys_getpid();
06631                         context->netbios_name = (char *)SMB_MALLOC(17);
06632                         if (!context->netbios_name) {
06633                                 errno = ENOMEM;
06634                                 return NULL;
06635                         }
06636                         slprintf(context->netbios_name, 16,
06637                                  "smbc%s%d", context->user, pid);
06638                 }
06639         }
06640 
06641         DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
06642 
06643         if (!context->workgroup) {
06644                 if (lp_workgroup()) {
06645                         context->workgroup = SMB_STRDUP(lp_workgroup());
06646                 }
06647                 else {
06648                         /* TODO: Think about a decent default workgroup */
06649                         context->workgroup = SMB_STRDUP("samba");
06650                 }
06651         }
06652 
06653         DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
06654                                         
06655         /* shortest timeout is 1 second */
06656         if (context->timeout > 0 && context->timeout < 1000) 
06657                 context->timeout = 1000;
06658 
06659         /*
06660          * FIXME: Should we check the function pointers here? 
06661          */
06662 
06663         context->internal->_initialized = True;
06664         
06665         return context;
06666 }
06667 
06668 
06669 /* Return the verion of samba, and thus libsmbclient */
06670 const char *
06671 smbc_version(void)
06672 {
06673         return samba_version_string();
06674 }

Sambaに対してSat Aug 29 21:23:05 2009に生成されました。  doxygen 1.4.7