"Fossies" - the Fresh Open Source Software Archive

Member "smbnetfs-0.6.3/src/smb_conn.c" (1 Feb 2018, 35192 Bytes) of package /linux/misc/smbnetfs-0.6.3.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "smb_conn.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.6.1_vs_0.6.2.

    1 #include "config.h"
    2 #include <time.h>
    3 #include <errno.h>
    4 #include <string.h>
    5 #include <stdarg.h>
    6 #include <stdlib.h>
    7 #include <sys/uio.h>
    8 #include <sys/mman.h>
    9 #include <sys/types.h>
   10 #include <sys/stat.h>
   11 #include <sys/socket.h>
   12 #include <fcntl.h>
   13 #include <signal.h>
   14 #include <pthread.h>
   15 #include <glib.h>
   16 
   17 #include "list.h"
   18 #include "common.h"
   19 #include "smbitem.h"
   20 #include "auth-libsecret.h"
   21 #include "auth.h"
   22 #include "smb_conn_proto.h"
   23 #include "process.h"
   24 #include "smb_conn.h"
   25 #include "neg_cache.h"
   26 
   27 #ifndef MAP_ANONYMOUS
   28     #define MAP_ANONYMOUS   MAP_ANON
   29 #endif
   30 
   31 #define SMB_CONN_PROCESS_STATE_UNKNOWN  -1
   32 #define SMB_CONN_PROCESS_STATE_ALIVE    0
   33 #define SMB_CONN_PROCESS_STATE_DIED 1
   34 
   35 struct smb_conn_query_result{
   36     int errno_value;
   37     int process_state;
   38 };
   39 
   40 
   41 static int      smb_conn_max_retry_count    = 2;
   42 static int      smb_conn_max_passwd_query_count = 10;
   43 static int      smb_conn_server_reply_timeout   = 60;
   44 
   45 static inline int smb_conn_is_neg_cache_candidate(enum smb_conn_cmd query_cmd,
   46                                                   struct smb_conn_query_result *result){
   47     if (result->process_state == SMB_CONN_PROCESS_STATE_DIED)
   48     switch(result->errno_value){
   49         case ETIMEDOUT:
   50         case ECONNREFUSED:
   51         case EHOSTUNREACH:
   52         return 1;
   53         case EINVAL:
   54         switch(query_cmd){
   55             case OPENDIR:
   56             case STAT:
   57             return 1;
   58             default:
   59             break;
   60         }
   61         default:
   62         break;
   63     }
   64     return 0;
   65 }
   66 
   67 static inline int smb_conn_query_result_map(struct smb_conn_query_result *result){
   68     if (result->process_state == SMB_CONN_PROCESS_STATE_ALIVE)
   69     return result->errno_value;
   70     return EIO;
   71 }
   72 
   73 static inline int smb_conn_query_result_check(enum smb_conn_cmd query_cmd,
   74                                               struct smb_conn_query_result *result){
   75     if (result->process_state == SMB_CONN_PROCESS_STATE_ALIVE)
   76     return 1;
   77     if (smb_conn_is_neg_cache_candidate(query_cmd, result))
   78     return 1;
   79     return 0;
   80 }
   81 
   82 int smb_conn_set_max_retry_count(int count){
   83     if (count < 0) return 0;
   84     DPRINTF(7, "count=%d\n", count);
   85     g_atomic_int_set(&smb_conn_max_retry_count, count);
   86     return 1;
   87 }
   88 
   89 static inline int smb_conn_get_max_retry_count(void){
   90     return g_atomic_int_get(&smb_conn_max_retry_count);
   91 }
   92 
   93 int smb_conn_set_max_passwd_query_count(int count){
   94     if (count < 3) return 0;
   95     DPRINTF(7, "count=%d\n", count);
   96     g_atomic_int_set(&smb_conn_max_passwd_query_count, count);
   97     return 1;
   98 }
   99 
  100 static inline int smb_conn_get_max_passwd_query_count(void){
  101     return g_atomic_int_get(&smb_conn_max_passwd_query_count);
  102 }
  103 
  104 int smb_conn_set_server_reply_timeout(int timeout){
  105     if (timeout < 10) return 0;
  106     DPRINTF(7, "timeout=%d\n", timeout);
  107     g_atomic_int_set(&smb_conn_server_reply_timeout, timeout);
  108     return 1;
  109 }
  110 
  111 static inline int smb_conn_get_server_reply_timeout(void){
  112     return g_atomic_int_get(&smb_conn_server_reply_timeout);
  113 }
  114 
  115 static void smb_conn_connection_close(struct smb_conn_ctx *ctx){
  116     LIST            *elem;
  117     struct smb_conn_file    *conn_file;
  118 
  119     if (ctx->shmem_ptr == NULL) return;
  120 
  121     process_kill_by_smb_conn_fd(ctx->conn_fd);
  122     ctx->conn_fd = -1;
  123 
  124     elem = first_list_elem(&ctx->smb_conn_file_list);
  125     while(is_valid_list_elem(&ctx->smb_conn_file_list, elem)){
  126     conn_file = list_entry(elem, struct smb_conn_file, entries);
  127     conn_file->access_time = 0;
  128     conn_file->srv_fd = NULL;
  129     elem = elem->next;
  130     }
  131 }
  132 
  133 static int smb_conn_up_if_broken(struct smb_conn_ctx *ctx){
  134     if (ctx->conn_fd != -1){
  135     if (process_is_smb_conn_alive(ctx->conn_fd)) return 0;
  136     smb_conn_connection_close(ctx);
  137     }
  138     ctx->conn_fd = process_start_new_smb_conn(ctx->shmem_ptr, ctx->shmem_size);
  139     return (ctx->conn_fd != -1) ? 0 : -1;
  140 }
  141 
  142 int smb_conn_ctx_init(struct smb_conn_ctx *ctx, size_t shmem_size){
  143     if ((ssize_t) shmem_size < getpagesize()) return -1;
  144 
  145     init_list(&ctx->smb_conn_file_list);
  146     pthread_mutex_init(&ctx->mutex, NULL);
  147     ctx->access_time = time(NULL);
  148     ctx->shmem_size = shmem_size;
  149     ctx->conn_fd = -1;
  150 
  151     if ((ctx->shmem_ptr = (char*) mmap(NULL, ctx->shmem_size,
  152         PROT_READ | PROT_WRITE,
  153         MAP_SHARED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED){
  154     pthread_mutex_destroy(&ctx->mutex);
  155     return -1;
  156     }
  157     return 0;
  158 }
  159 
  160 int smb_conn_ctx_destroy(struct smb_conn_ctx *ctx){
  161     int result = -1;
  162 
  163     if (ctx->shmem_ptr == NULL) return -1;
  164 
  165     pthread_mutex_lock(&ctx->mutex);
  166     if (is_list_empty(&ctx->smb_conn_file_list)){
  167     if (ctx->conn_fd != -1) smb_conn_connection_close(ctx);
  168     munmap(ctx->shmem_ptr, ctx->shmem_size);
  169     ctx->shmem_ptr = NULL;
  170     result = 0;
  171     }
  172     pthread_mutex_unlock(&ctx->mutex);
  173     if (result == 0) pthread_mutex_destroy(&ctx->mutex);
  174     return result;
  175 }
  176 
  177 static int smb_conn_send_password_base(struct smb_conn_ctx *ctx, const char *domain,
  178                 const char *user, const char *password){
  179     ssize_t         bytes;
  180     struct iovec        iov[5];
  181     struct smb_conn_query_hdr   header;
  182     struct smb_conn_passwd  data;
  183 
  184     if ((ctx == NULL) || (ctx->conn_fd == -1)) return -1;
  185 
  186     iov[0].iov_base = &header;
  187     iov[0].iov_len  = sizeof(header);
  188     iov[1].iov_base = &data;
  189     iov[1].iov_len  = sizeof(data);
  190     iov[2].iov_base = (char*) domain;
  191     iov[2].iov_len  = strlen(domain) + 1;
  192     iov[3].iov_base = (char*) user;
  193     iov[3].iov_len  = strlen(user) + 1;
  194     iov[4].iov_base = (char*) password;
  195     iov[4].iov_len  = strlen(password) + 1;
  196 
  197     header.query_len   = iov[0].iov_len + iov[1].iov_len +
  198              iov[2].iov_len + iov[3].iov_len + iov[4].iov_len;
  199     header.query_cmd   = PASSWORD;
  200     header.debug_level = common_get_smbnetfs_debug_level();
  201     data.domain_offs   = sizeof(data);
  202     data.username_offs = sizeof(data) + iov[2].iov_len;
  203     data.password_offs = sizeof(data) + iov[2].iov_len + iov[3].iov_len;
  204 
  205     if (header.query_len <= COMM_BUF_SIZE){
  206     /* send password data */
  207     bytes = writev(ctx->conn_fd, iov, 5);
  208     }else{
  209     bytes = -1;
  210     }
  211     return (bytes == (ssize_t) header.query_len) ? 0 : -1;
  212 }
  213 
  214 static int smb_conn_send_password(struct smb_conn_ctx *ctx,
  215             const char *server, const char *share){
  216 
  217 #ifdef HAVE_LIBSECRET
  218     struct libsecret_authinfo       *libsecret_info;
  219 #endif /* HAVE_LIBSECRET */
  220     struct authinfo         *config_file_info;
  221     int                 config_file_info_suitability;
  222     char                workgroup[256];
  223     int                 ret;
  224 
  225     if ((ctx == NULL) || (ctx->conn_fd == -1)) return -1;
  226 
  227     memset(workgroup, 0, sizeof(workgroup));
  228     smbitem_get_group(server, workgroup, sizeof(workgroup));
  229 
  230     config_file_info_suitability = -1;
  231     config_file_info = auth_get_authinfo(
  232                 workgroup, server, share,
  233                 &config_file_info_suitability);
  234     if ((config_file_info != NULL) &&
  235     ((config_file_info->domain   == NULL) ||
  236      (config_file_info->user     == NULL) ||
  237      (config_file_info->password == NULL))){
  238 
  239     DPRINTF(0, "WARNING!!! Damaged authinfo record\n");
  240     auth_release_authinfo(config_file_info);
  241     config_file_info = NULL;
  242     config_file_info_suitability = -1;
  243     }
  244 
  245 #ifdef HAVE_LIBSECRET
  246     libsecret_info = libsecret_get_authinfo(workgroup, server, share);
  247     if ((libsecret_info != NULL) &&
  248     ((libsecret_info->domain   == NULL) ||
  249      (libsecret_info->user     == NULL) ||
  250      (libsecret_info->password == NULL))){
  251 
  252     DPRINTF(0, "WARNING!!! Damaged libsecret_info record\n");
  253     libsecret_free_authinfo(libsecret_info);
  254     libsecret_info = NULL;
  255     }
  256 
  257     if (libsecret_info != NULL){
  258     if (libsecret_info->suitability >= config_file_info_suitability){
  259         if (config_file_info != NULL)
  260         auth_release_authinfo(config_file_info);
  261         config_file_info = NULL;
  262         config_file_info_suitability = -1;
  263         goto use_libsecret_info;
  264     }
  265     libsecret_free_authinfo(libsecret_info);
  266     libsecret_info = NULL;
  267     }
  268 #endif /* HAVE_LIBSECRET */
  269 
  270     if (config_file_info == NULL) return -1;
  271     ret = smb_conn_send_password_base(ctx,
  272             config_file_info->domain,
  273             config_file_info->user,
  274             config_file_info->password);
  275     auth_release_authinfo(config_file_info);
  276     return ret;
  277 
  278 #ifdef HAVE_LIBSECRET
  279   use_libsecret_info:
  280     ret = smb_conn_send_password_base(ctx,
  281             libsecret_info->domain,
  282             libsecret_info->user,
  283             libsecret_info->password);
  284     libsecret_free_authinfo(libsecret_info);
  285     return ret;
  286 #endif /* HAVE_LIBSECRET */
  287 }
  288 
  289 static int smb_conn_process_query_lowlevel_va(
  290             struct smb_conn_ctx *ctx,
  291             enum smb_conn_cmd query_cmd,
  292             void *query, size_t query_len,
  293             struct smb_conn_query_result *result,
  294             void *reply, size_t reply_len,
  295             va_list ap){
  296 
  297     int             iov_cnt, retval, count;
  298     ssize_t         bytes;
  299     struct iovec        iov[4];
  300     struct smb_conn_query_hdr   query_header;
  301 
  302     if ((ctx == NULL) || (ctx->conn_fd == -1) ||
  303     ((query == NULL) || (query_len == 0)) ||
  304     (result == NULL) ||
  305     ((reply == NULL) && (reply_len != 0))) return EINVAL;
  306 
  307     result->process_state = SMB_CONN_PROCESS_STATE_UNKNOWN;
  308     result->errno_value = EINVAL;
  309 
  310     iov_cnt = 2;
  311     query_header.query_cmd = query_cmd;
  312     query_header.debug_level = common_get_smbnetfs_debug_level();
  313     query_header.query_len = sizeof(query_header) + query_len;
  314 
  315     iov[0].iov_base = &query_header;
  316     iov[0].iov_len  = sizeof(query_header);
  317     iov[1].iov_base = query;
  318     iov[1].iov_len  = query_len;
  319 
  320     while(1){
  321     const char  *str;
  322 
  323     str = va_arg(ap, const char *);
  324     if (str == NULL) break;
  325     if (iov_cnt >= (ssize_t) (sizeof(iov) / sizeof(struct iovec))){
  326         va_end(ap);
  327         return EINVAL;
  328     }
  329 
  330     iov[iov_cnt].iov_base = (void *) str;
  331     iov[iov_cnt].iov_len  = strlen(str) + 1;
  332     query_header.query_len += iov[iov_cnt].iov_len;
  333     iov_cnt++;
  334     }
  335 
  336     if (query_header.query_len > COMM_BUF_SIZE) return EIO;
  337 
  338     /* send query */
  339     bytes = writev(ctx->conn_fd, iov, iov_cnt);
  340     if (bytes != (ssize_t) query_header.query_len) goto error;
  341 
  342     count = 0;
  343     while(1){
  344     fd_set              readfds, exceptfds;
  345     struct timeval          tv;
  346     struct smb_conn_reply_hdr   *reply_hdr;
  347     char                buf[COMM_BUF_SIZE];
  348 
  349     tv.tv_sec = smb_conn_get_server_reply_timeout();
  350     tv.tv_usec = 0;
  351 
  352     FD_ZERO(&readfds);
  353     FD_SET(ctx->conn_fd, &readfds);
  354 
  355     FD_ZERO(&exceptfds);
  356     FD_SET(ctx->conn_fd, &exceptfds);
  357 
  358     /* wait for reply */
  359     retval = select(ctx->conn_fd + 1, &readfds, NULL, &exceptfds, &tv);
  360     if ((retval <= 0) || FD_ISSET(ctx->conn_fd, &exceptfds)) goto error;
  361 
  362     /* read reply */
  363     bytes = read(ctx->conn_fd, buf, COMM_BUF_SIZE);
  364     if (bytes < (ssize_t) sizeof(struct smb_conn_reply_hdr)) goto error;
  365 
  366     /* check reply */
  367     reply_hdr = (struct smb_conn_reply_hdr *) buf;
  368     if ((ssize_t) reply_hdr->reply_len != bytes) goto error;
  369 
  370     /* is it message? */
  371     if ((reply_hdr->reply_cmd == MESSAGE) ||
  372         (reply_hdr->reply_cmd == DIE_MSG)){
  373 
  374         const char          *msg;
  375         struct smb_conn_message_req *msg_req;
  376 
  377         if ((reply_hdr->reply_cmd == MESSAGE) &&
  378         (reply_hdr->errno_value != 0)) goto error;
  379 
  380         if (buf[bytes - 1] != '\0' ) goto error;
  381         bytes -= sizeof(struct smb_conn_reply_hdr);
  382         if (bytes < (ssize_t) sizeof(struct smb_conn_message_req))
  383         goto error;
  384 
  385         msg_req = (struct smb_conn_message_req *) (reply_hdr + 1);
  386         if (msg_req->msg_offs != sizeof(struct smb_conn_message_req))
  387         goto error;
  388         bytes -= sizeof(struct smb_conn_message_req);
  389 
  390         msg = ((char *) msg_req) + msg_req->msg_offs;
  391         if (bytes != (ssize_t) (strlen(msg) + 1)) goto error;
  392 
  393         DEBUG_PRINT(msg_req->debug_level, "%s", msg);
  394 
  395         if (reply_hdr->reply_cmd == DIE_MSG){
  396         result->errno_value   = reply_hdr->errno_value;
  397         result->process_state = SMB_CONN_PROCESS_STATE_DIED;
  398         smb_conn_connection_close(ctx);
  399         return 0;
  400         }
  401         continue;
  402     }
  403 
  404     /* is it password request? */
  405     if (reply_hdr->reply_cmd == PASSWORD){
  406         const char          *server, *share;
  407         struct smb_conn_passwd_req  *passwd_req;
  408 
  409         /* infinite loop? */
  410         if (count > smb_conn_get_max_passwd_query_count()) goto error;
  411 
  412         if (reply_hdr->errno_value != 0) goto error;
  413         if (buf[bytes - 1] != '\0' ) goto error;
  414         bytes -= sizeof(struct smb_conn_reply_hdr);
  415         if (bytes < (ssize_t) sizeof(struct smb_conn_passwd_req)) goto error;
  416 
  417         passwd_req = (struct smb_conn_passwd_req *) (reply_hdr + 1);
  418         if ((passwd_req->server_offs != sizeof(struct smb_conn_passwd_req)) ||
  419         (passwd_req->share_offs  <= passwd_req->server_offs) ||
  420         ((ssize_t) passwd_req->share_offs  >  bytes - 1)) goto error;
  421         bytes -= sizeof(struct smb_conn_passwd_req);
  422 
  423         server = ((char *) passwd_req) + passwd_req->server_offs;
  424         share  = ((char *) passwd_req) + passwd_req->share_offs;
  425         if (bytes != (ssize_t) (strlen(server) + strlen(share) + 2))
  426         goto error;
  427 
  428         /* process password request */
  429         count++;
  430         if (smb_conn_send_password(ctx, server, share) != 0) goto error;
  431         continue;
  432     }
  433 
  434     /* it should be our reply */
  435     if (reply_hdr->reply_cmd != query_cmd) goto error;
  436     if (reply_hdr->errno_value != 0) reply_len = 0;
  437     if (bytes != (ssize_t) (sizeof(struct smb_conn_reply_hdr) + reply_len))
  438         goto error;
  439 
  440     /* ok, we got a reply, store error/reply and exit */
  441     if (reply_len != 0) memcpy(reply, reply_hdr + 1, reply_len);
  442     result->errno_value   = reply_hdr->errno_value;
  443     result->process_state = SMB_CONN_PROCESS_STATE_ALIVE;
  444     return 0;
  445     }
  446 
  447   error:
  448     smb_conn_connection_close(ctx);
  449     return EIO;
  450 }
  451 
  452 static int smb_conn_process_query_lowlevel(
  453             struct smb_conn_ctx *ctx,
  454             enum smb_conn_cmd query_cmd,
  455             void *query, size_t query_len,
  456             struct smb_conn_query_result *result,
  457             void *reply, size_t reply_len,
  458             ...){
  459     va_list ap;
  460     int     retval;
  461 
  462     va_start(ap, reply_len);
  463     retval = smb_conn_process_query_lowlevel_va(
  464             ctx,
  465             query_cmd,
  466             query, query_len,
  467             result,
  468             reply, reply_len,
  469             ap);
  470     va_end(ap);
  471     return retval;
  472 }
  473 
  474 static int smb_conn_process_query(
  475             struct smb_conn_ctx *ctx,
  476             enum smb_conn_cmd query_cmd,
  477             void *query, size_t query_len,
  478             void *reply, size_t reply_len,
  479             ...){
  480 
  481     va_list             ap;
  482     int                 count, retval;
  483     struct smb_conn_query_result    result;
  484     const char              *url = NULL;
  485 
  486     if ((query_cmd >= OPEN) && (query_cmd <= REMOVEXATTR)){
  487     va_start(ap, reply_len);
  488     url = va_arg(ap, const char *);
  489     va_end(ap);
  490 
  491     if (url != NULL){
  492         retval = neg_cache_check(url);
  493         if (retval != 0){
  494         errno = retval;
  495         return retval;
  496         }
  497     }
  498     }
  499 
  500     for(count = 0; ; count++){
  501     if (smb_conn_up_if_broken(ctx) != 0) break;
  502 
  503     va_start(ap, reply_len);
  504     retval = smb_conn_process_query_lowlevel_va(
  505             ctx,
  506             query_cmd,
  507             query, query_len,
  508             &result,
  509             reply, reply_len,
  510             ap);
  511     va_end(ap);
  512     if ((retval == 0) && smb_conn_query_result_check(query_cmd, &result)){
  513         retval = smb_conn_query_result_map(&result);
  514         if (smb_conn_is_neg_cache_candidate(query_cmd, &result) && (url != NULL))
  515         neg_cache_store(url, retval);
  516         return retval;
  517     }
  518 
  519     if (count >= smb_conn_get_max_retry_count()) break;
  520     sleep(2);
  521     }
  522     if (url != NULL) neg_cache_store(url, EIO);
  523     return EIO;
  524 }
  525 
  526 static int smb_conn_process_fd_query(
  527             struct smb_conn_ctx *ctx,
  528             enum smb_conn_cmd query_cmd,
  529             struct smb_conn_file *file,
  530             smb_conn_srv_fd *query_fd_ptr,
  531             void *query, size_t query_len,
  532             void *reply, size_t reply_len){
  533 
  534     int                 count, retval;
  535     struct smb_conn_query_result    result;
  536 
  537     if ((file == NULL) || (file->url == NULL)) return EINVAL;
  538 
  539     for(count = 0; ; count++){
  540     if (smb_conn_up_if_broken(ctx) != 0) break;
  541 
  542     if (file->srv_fd == NULL){
  543         union{
  544         struct smb_conn_open_query  open;
  545         struct smb_conn_url_query   opendir;
  546         } fd_query;
  547         size_t          fd_len;
  548         struct smb_conn_fd_reply    fd_reply;
  549 
  550         switch(file->reopen_cmd){
  551         case OPEN:
  552             fd_len = sizeof(fd_query.open);
  553 
  554             fd_query.open.url_offs = sizeof(fd_query.open);
  555             fd_query.open.mode     = 0664;
  556             fd_query.open.flags    = file->reopen_flags & (~(O_CREAT | O_TRUNC));
  557             break;
  558 
  559         case OPENDIR:
  560             /* we cant reopen directory with non-zero offset, so return EIO */
  561             if (file->reopen_flags != 0) return EIO;
  562 
  563             fd_len = sizeof(fd_query.opendir);
  564 
  565             fd_query.opendir.url_offs = sizeof(fd_query.opendir);
  566             break;
  567 
  568         default:
  569             return EIO;
  570         }
  571 
  572         retval = smb_conn_process_query_lowlevel(
  573             ctx,
  574             file->reopen_cmd,
  575             &fd_query, fd_len,
  576             &result,
  577             &fd_reply, sizeof(fd_reply),
  578             file->url, NULL);
  579         if ((retval != 0) || !smb_conn_query_result_check(file->reopen_cmd, &result)) goto loop_end;
  580         if (smb_conn_query_result_map(&result) != 0)
  581         return smb_conn_query_result_map(&result);
  582     
  583         file->srv_fd = fd_reply.srv_fd;
  584     }
  585 
  586     *query_fd_ptr = file->srv_fd;
  587     retval = smb_conn_process_query_lowlevel(
  588             ctx,
  589             query_cmd,
  590             query, query_len,
  591             &result,
  592             reply, reply_len,
  593             NULL);
  594     if ((retval == 0) && smb_conn_query_result_check(query_cmd, &result))
  595         return smb_conn_query_result_map(&result);
  596 
  597       loop_end:
  598     if (count >= smb_conn_get_max_retry_count()) break;
  599     sleep(2);
  600     }
  601     return EIO;
  602 }
  603 
  604 smb_conn_fd smb_conn_open(struct smb_conn_ctx *ctx,
  605                 const char *url, int flags, mode_t mode){
  606 
  607     int                 error;
  608     struct smb_conn_file        *file;
  609     struct smb_conn_open_query      query;
  610     struct smb_conn_fd_reply        reply;
  611 
  612     file = malloc(sizeof(struct smb_conn_file) + strlen(url) + 1);
  613     if (file == NULL){
  614     errno = ENOMEM;
  615     return NULL;
  616     }
  617 
  618     memset(&file->entries, 0, sizeof(LIST));
  619     file->access_time  = (time_t) 0;
  620     file->ctx          = ctx;
  621     file->url          = ((char *) file) + sizeof(struct smb_conn_file);
  622     file->reopen_cmd   = OPEN;
  623     file->reopen_flags = flags & ~(O_CREAT | O_EXCL | O_TRUNC);
  624     file->srv_fd       = NULL;
  625     strcpy(file->url, url);
  626 
  627     query.url_offs = sizeof(struct smb_conn_open_query);
  628     query.mode     = mode;
  629     query.flags    = flags;
  630 
  631     pthread_mutex_lock(&ctx->mutex);
  632     ctx->access_time = file->access_time = time(NULL);
  633     error = smb_conn_process_query(
  634             ctx, OPEN,
  635             &query, sizeof(query),
  636             &reply, sizeof(reply),
  637             url, NULL);
  638     if (error == 0){
  639     file->srv_fd = reply.srv_fd;
  640     add_to_list(&ctx->smb_conn_file_list, &file->entries);
  641     }else{
  642     free(file);
  643     file = NULL;
  644     }
  645     pthread_mutex_unlock(&ctx->mutex);
  646     if (error != 0) errno = error;
  647     return file;
  648 }
  649 
  650 smb_conn_fd smb_conn_creat(struct smb_conn_ctx *ctx,
  651                 const char *url, mode_t mode){
  652 
  653     int                 error;
  654     struct smb_conn_file        *file;
  655     struct smb_conn_url_mode_query  query;
  656     struct smb_conn_fd_reply        reply;
  657 
  658     file = malloc(sizeof(struct smb_conn_file) + strlen(url) + 1);
  659     if (file == NULL){
  660     errno = ENOMEM;
  661     return NULL;
  662     }
  663 
  664     memset(&file->entries, 0, sizeof(LIST));
  665     file->access_time  = (time_t) 0;
  666     file->ctx          = ctx;
  667     file->url          = ((char *) file) + sizeof(struct smb_conn_file);
  668     file->reopen_cmd   = OPEN;
  669     file->reopen_flags = O_WRONLY;
  670     file->srv_fd       = NULL;
  671     strcpy(file->url, url);
  672 
  673     query.url_offs = sizeof(struct smb_conn_url_mode_query);
  674     query.mode     = mode;
  675 
  676     pthread_mutex_lock(&ctx->mutex);
  677     ctx->access_time = file->access_time = time(NULL);
  678     error = smb_conn_process_query(
  679             ctx, CREAT,
  680             &query, sizeof(query),
  681             &reply, sizeof(reply),
  682             url, NULL);
  683     if (error == 0){
  684     file->srv_fd = reply.srv_fd;
  685     add_to_list(&ctx->smb_conn_file_list, &file->entries);
  686     }else{
  687     free(file);
  688     file = NULL;
  689     }
  690     pthread_mutex_unlock(&ctx->mutex);
  691     if (error != 0) errno = error;
  692     return file;
  693 }
  694 
  695 ssize_t smb_conn_read(struct smb_conn_ctx *ctx,
  696             smb_conn_fd fd, off_t offset,
  697             void *buf, size_t bufsize){
  698 
  699     int                 error;
  700     struct smb_conn_file        *file;
  701     struct smb_conn_rw_query        query;
  702     struct smb_conn_buf_reply       reply;
  703 
  704     if ((fd == NULL) || (bufsize > ctx->shmem_size)){
  705     errno = EINVAL;
  706     return -1;
  707     }
  708 
  709     /* query.fd will be substituted in smb_conn_process_fd_query() */
  710     query.offset  = offset;
  711     query.bufsize = bufsize;
  712 
  713     error = EINVAL;
  714     reply.bufsize = -1;
  715     file = (struct smb_conn_file *) fd;
  716 
  717     pthread_mutex_lock(&ctx->mutex);
  718     if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
  719     ctx->access_time = file->access_time = time(NULL);
  720     error = smb_conn_process_fd_query(
  721             ctx, READ, file,
  722             &query.srv_fd,
  723             &query, sizeof(query),
  724             &reply, sizeof(reply));
  725     if ((error == 0) && (reply.bufsize <= (ssize_t) bufsize)){
  726         memcpy(buf, ctx->shmem_ptr, reply.bufsize);
  727     }else{
  728         reply.bufsize = -1;
  729         if (error == 0) error = EIO;
  730     }
  731     }
  732     pthread_mutex_unlock(&ctx->mutex);
  733     if (error != 0) errno = error;
  734     return reply.bufsize;
  735 }
  736 
  737 ssize_t smb_conn_write(struct smb_conn_ctx *ctx,
  738             smb_conn_fd fd, off_t offset,
  739             const void *buf, size_t bufsize){
  740 
  741     int                 error;
  742     struct smb_conn_file        *file;
  743     struct smb_conn_rw_query        query;
  744     struct smb_conn_buf_reply       reply;
  745 
  746     if ((fd == NULL) || (bufsize > ctx->shmem_size)){
  747     errno = EINVAL;
  748     return -1;
  749     }
  750 
  751     /* query.fd will be substituted in smb_conn_process_fd_query() */
  752     query.offset  = offset;
  753     query.bufsize = bufsize;
  754 
  755     error = EINVAL;
  756     reply.bufsize = -1;
  757     file = (struct smb_conn_file *) fd;
  758 
  759     pthread_mutex_lock(&ctx->mutex);
  760     if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
  761     ctx->access_time = file->access_time = time(NULL);
  762     memcpy(ctx->shmem_ptr, buf, bufsize);
  763     msync(ctx->shmem_ptr, bufsize, MS_SYNC);
  764     error = smb_conn_process_fd_query(
  765             ctx, WRITE, file,
  766             &query.srv_fd,
  767             &query, sizeof(query),
  768             &reply, sizeof(reply));
  769     if ((error != 0) || (reply.bufsize > (ssize_t) bufsize)){
  770         reply.bufsize = -1;
  771         if (error == 0) error = EIO;
  772     }
  773     }
  774     pthread_mutex_unlock(&ctx->mutex);
  775     if (error != 0) errno = error;
  776     return reply.bufsize;
  777 }
  778 
  779 int smb_conn_close(struct smb_conn_ctx *ctx, smb_conn_fd fd){
  780     int                 error;
  781     struct smb_conn_file        *file;
  782 
  783     if (fd == NULL){
  784     errno = EINVAL;
  785     return -1;
  786     }
  787 
  788     error = EINVAL;
  789     file = (struct smb_conn_file *) fd;
  790     pthread_mutex_lock(&ctx->mutex);
  791     if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
  792     ctx->access_time = time(NULL);
  793     if (file->srv_fd != NULL){
  794         struct smb_conn_query_result    result;
  795         struct smb_conn_fd_query        query;
  796 
  797         query.srv_fd = file->srv_fd;
  798         smb_conn_process_query_lowlevel(
  799             ctx,
  800             CLOSE,
  801             &query, sizeof(query),
  802             &result,
  803             NULL, 0,
  804             NULL);
  805     }
  806     remove_from_list(&ctx->smb_conn_file_list, &file->entries);
  807     free(file);
  808     error = 0;
  809     }
  810     pthread_mutex_unlock(&ctx->mutex);
  811     if (error != 0) errno = error;
  812     return (error != 0) ? -1 : 0;
  813 }
  814 
  815 int smb_conn_unlink(struct smb_conn_ctx *ctx, const char *url){
  816     int                 error;
  817     struct smb_conn_url_query       query;
  818 
  819     query.url_offs = sizeof(struct smb_conn_url_query);
  820 
  821     pthread_mutex_lock(&ctx->mutex);
  822     ctx->access_time = time(NULL);
  823     error = smb_conn_process_query(
  824             ctx, UNLINK,
  825             &query, sizeof(query),
  826             NULL, 0,
  827             url, NULL);
  828     pthread_mutex_unlock(&ctx->mutex);
  829     if (error != 0){
  830     errno = error;
  831     return -1;
  832     }
  833     return 0;
  834 }
  835 
  836 int smb_conn_rename(struct smb_conn_ctx *ctx, const char *old_url, const char *new_url){
  837     int                 error;
  838     struct smb_conn_rename_query    query;
  839 
  840     query.old_url_offs = sizeof(struct smb_conn_rename_query);
  841     query.new_url_offs = sizeof(struct smb_conn_rename_query) + strlen(old_url) + 1;
  842 
  843     pthread_mutex_lock(&ctx->mutex);
  844     ctx->access_time = time(NULL);
  845     error = smb_conn_process_query(
  846             ctx, RENAME,
  847             &query, sizeof(query),
  848             NULL, 0,
  849             old_url, new_url, NULL);
  850     pthread_mutex_unlock(&ctx->mutex);
  851     if (error != 0){
  852     errno = error;
  853     return -1;
  854     }
  855     return 0;
  856 }
  857 
  858 smb_conn_fd smb_conn_opendir(struct smb_conn_ctx *ctx,
  859                 const char *url){
  860 
  861     int                 error;
  862     struct smb_conn_file        *file;
  863     struct smb_conn_url_query       query;
  864     struct smb_conn_fd_reply        reply;
  865 
  866     file = malloc(sizeof(struct smb_conn_file) + strlen(url) + 1);
  867     if (file == NULL){
  868     errno = ENOMEM;
  869     return NULL;
  870     }
  871 
  872     memset(&file->entries, 0, sizeof(LIST));
  873     file->access_time  = (time_t) 0;
  874     file->ctx          = ctx;
  875     file->url          = ((char *) file) + sizeof(struct smb_conn_file);
  876     file->reopen_cmd   = OPENDIR;
  877     file->reopen_flags = 0;
  878     file->srv_fd       = NULL;
  879     strcpy(file->url, url);
  880 
  881     query.url_offs = sizeof(struct smb_conn_url_query);
  882 
  883     pthread_mutex_lock(&ctx->mutex);
  884     ctx->access_time = file->access_time = time(NULL);
  885     error = smb_conn_process_query(
  886             ctx, OPENDIR,
  887             &query, sizeof(query),
  888             &reply, sizeof(reply),
  889             url, NULL);
  890     if (error == 0){
  891     add_to_list(&ctx->smb_conn_file_list, &file->entries);
  892     file->srv_fd      = reply.srv_fd;
  893     }else{
  894     free(file);
  895     file = NULL;
  896     }
  897     pthread_mutex_unlock(&ctx->mutex);
  898     if (error != 0) errno = error;
  899     return file;
  900 }
  901 
  902 int smb_conn_closedir(struct smb_conn_ctx *ctx, smb_conn_fd fd){
  903     int                 error;
  904     struct smb_conn_file        *file;
  905 
  906     if (fd == NULL){
  907     errno = EINVAL;
  908     return -1;
  909     }
  910 
  911     error = EINVAL;
  912     file = (struct smb_conn_file *) fd;
  913     pthread_mutex_lock(&ctx->mutex);
  914     if ((file->reopen_cmd == OPENDIR) && (file->ctx == ctx)){
  915     ctx->access_time = time(NULL);
  916     if (file->srv_fd != NULL){
  917         struct smb_conn_query_result    result;
  918         struct smb_conn_fd_query        query;
  919 
  920         query.srv_fd = file->srv_fd;
  921         smb_conn_process_query_lowlevel(
  922             ctx,
  923             CLOSEDIR,
  924             &query, sizeof(query),
  925             &result,
  926             NULL, 0,
  927             NULL);
  928     }
  929     remove_from_list(&ctx->smb_conn_file_list, &file->entries);
  930     free(file);
  931     error = 0;
  932     }
  933     pthread_mutex_unlock(&ctx->mutex);
  934     if (error != 0) errno = error;
  935     return (error != 0) ? -1 : 0;
  936 }
  937 
  938 ssize_t smb_conn_readdir(struct smb_conn_ctx *ctx,
  939             smb_conn_fd fd, void *buf, size_t bufsize){
  940 
  941     int                 error;
  942     struct smb_conn_file        *file;
  943     struct smb_conn_rw_query        query;
  944     struct smb_conn_buf_reply       reply;
  945 
  946     if ((fd == NULL) || (bufsize > ctx->shmem_size)){
  947     errno = EINVAL;
  948     return -1;
  949     }
  950 
  951     /* query.fd will be substituted in smb_conn_process_fd_query() */
  952     query.offset  = (off_t) (-1);
  953     query.bufsize = bufsize;
  954 
  955     error = EINVAL;
  956     reply.bufsize = -1;
  957     file = (struct smb_conn_file *) fd;
  958 
  959     pthread_mutex_lock(&ctx->mutex);
  960     if ((file->reopen_cmd == OPENDIR) && (file->ctx == ctx)){
  961     ctx->access_time = file->access_time = time(NULL);
  962 
  963     /* we cant reopen directory with non-zero offset, so use               */
  964     /* file->reopen_flags for indication of zero/non-zero directory offset */
  965     if (file->reopen_flags == 0){
  966         /* =================================== */
  967         /* reading from zero offset, use       */
  968         /* function with connection recovery   */
  969         /* =================================== */
  970         error = smb_conn_process_fd_query(
  971             ctx,
  972             READDIR,
  973             file,
  974             &query.srv_fd,
  975             &query, sizeof(query),
  976             &reply, sizeof(reply));
  977     }else{
  978         /* =================================== */
  979         /* reading from non-zero offset,       */
  980         /* connection recovery is not possible */
  981         /* =================================== */
  982 
  983         struct smb_conn_query_result    result;
  984 
  985         if (file->srv_fd == NULL){
  986         errno = EIO;
  987         return -1;
  988         }
  989 
  990         query.srv_fd  = file->srv_fd;
  991         error = smb_conn_process_query_lowlevel(
  992             ctx,
  993             READDIR,
  994             &query, sizeof(query),
  995             &result,
  996             &reply, sizeof(reply),
  997             NULL);
  998         if (error == 0) error = smb_conn_query_result_map(&result);
  999     }
 1000     if ((error == 0) && (reply.bufsize <= (ssize_t) bufsize) &&
 1001         (reply.bufsize % sizeof(struct smb_conn_dirent_rec) == 0)){
 1002         memcpy(buf, ctx->shmem_ptr, reply.bufsize);
 1003         file->reopen_flags++;
 1004     }else{
 1005         reply.bufsize = -1;
 1006         if (error == 0) error = EIO;
 1007     }
 1008     }
 1009     pthread_mutex_unlock(&ctx->mutex);
 1010     if (error != 0) errno = error;
 1011     return reply.bufsize;
 1012 }
 1013 
 1014 int smb_conn_mkdir(struct smb_conn_ctx *ctx, const char *url, mode_t mode){
 1015     int                 error;
 1016     struct smb_conn_url_mode_query  query;
 1017 
 1018     query.url_offs = sizeof(struct smb_conn_url_mode_query);
 1019     query.mode     = mode;
 1020 
 1021     pthread_mutex_lock(&ctx->mutex);
 1022     ctx->access_time = time(NULL);
 1023     error = smb_conn_process_query(
 1024             ctx, MKDIR,
 1025             &query, sizeof(query),
 1026             NULL, 0,
 1027             url, NULL);
 1028     pthread_mutex_unlock(&ctx->mutex);
 1029     if (error != 0){
 1030     errno = error;
 1031     return -1;
 1032     }
 1033     return 0;
 1034 }
 1035 
 1036 int smb_conn_rmdir(struct smb_conn_ctx *ctx, const char *url){
 1037     int                 error;
 1038     struct smb_conn_url_query       query;
 1039 
 1040     query.url_offs = sizeof(struct smb_conn_url_query);
 1041 
 1042     pthread_mutex_lock(&ctx->mutex);
 1043     ctx->access_time = time(NULL);
 1044     error = smb_conn_process_query(
 1045             ctx, RMDIR,
 1046             &query, sizeof(query),
 1047             NULL, 0,
 1048             url, NULL);
 1049     pthread_mutex_unlock(&ctx->mutex);
 1050     if (error != 0){
 1051     errno = error;
 1052     return -1;
 1053     }
 1054     return 0;
 1055 }
 1056 
 1057 int smb_conn_stat(struct smb_conn_ctx *ctx, const char *url, struct stat *st){
 1058     int                 error;
 1059     struct smb_conn_url_query       query;
 1060     struct smb_conn_stat_reply      reply;
 1061 
 1062     query.url_offs = sizeof(struct smb_conn_url_query);
 1063 
 1064     pthread_mutex_lock(&ctx->mutex);
 1065     ctx->access_time = time(NULL);
 1066     error = smb_conn_process_query(
 1067             ctx, STAT,
 1068             &query, sizeof(query),
 1069             &reply, sizeof(reply),
 1070             url, NULL);
 1071     pthread_mutex_unlock(&ctx->mutex);
 1072     if (error != 0){
 1073     errno = error;
 1074     return -1;
 1075     }
 1076     memcpy(st, &reply.stat, sizeof(struct stat));
 1077     return 0;
 1078 }
 1079 
 1080 int smb_conn_fstat(struct smb_conn_ctx *ctx,
 1081             smb_conn_fd fd, struct stat *st){
 1082 
 1083     int                 error;
 1084     struct smb_conn_file        *file;
 1085     struct smb_conn_fd_query        query;
 1086     struct smb_conn_stat_reply      reply;
 1087 
 1088     if (fd == NULL){
 1089     errno = EINVAL;
 1090     return -1;
 1091     }
 1092 
 1093     /* query.fd will be substituted in smb_conn_process_fd_query() */
 1094 
 1095     error = EINVAL;
 1096     file = (struct smb_conn_file *) fd;
 1097 
 1098     pthread_mutex_lock(&ctx->mutex);
 1099     if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
 1100     ctx->access_time = file->access_time = time(NULL);
 1101     error = smb_conn_process_fd_query(
 1102             ctx, FSTAT, file,
 1103             &query.srv_fd,
 1104             &query, sizeof(query),
 1105             &reply, sizeof(reply));
 1106     }
 1107     pthread_mutex_unlock(&ctx->mutex);
 1108     if (error != 0){
 1109     errno = error;
 1110     return -1;
 1111     }
 1112     memcpy(st, &reply.stat, sizeof(struct stat));
 1113     return 0;
 1114 }
 1115 
 1116 int smb_conn_ftruncate(struct smb_conn_ctx *ctx,
 1117             smb_conn_fd fd, off_t size){
 1118 
 1119     int                 error;
 1120     struct smb_conn_file        *file;
 1121     struct smb_conn_ftruncate_query query;
 1122 
 1123     if (fd == NULL){
 1124     errno = EINVAL;
 1125     return -1;
 1126     }
 1127 
 1128     /* query.fd will be substituted in smb_conn_process_fd_query() */
 1129     query.offset = size;
 1130 
 1131     error = EINVAL;
 1132     file = (struct smb_conn_file *) fd;
 1133 
 1134     pthread_mutex_lock(&ctx->mutex);
 1135     if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
 1136     ctx->access_time = file->access_time = time(NULL);
 1137     error = smb_conn_process_fd_query(
 1138             ctx, FTRUNCATE, file,
 1139             &query.srv_fd,
 1140             &query, sizeof(query),
 1141             NULL, 0);
 1142     }
 1143     pthread_mutex_unlock(&ctx->mutex);
 1144     if (error != 0){
 1145     errno = error;
 1146     return -1;
 1147     }
 1148     return 0;
 1149 }
 1150 
 1151 int smb_conn_chmod(struct smb_conn_ctx *ctx, const char *url, mode_t mode){
 1152     int                 error;
 1153     struct smb_conn_url_mode_query  query;
 1154 
 1155     query.url_offs = sizeof(struct smb_conn_url_mode_query);
 1156     query.mode     = mode;
 1157 
 1158     pthread_mutex_lock(&ctx->mutex);
 1159     ctx->access_time = time(NULL);
 1160     error = smb_conn_process_query(
 1161             ctx, CHMOD,
 1162             &query, sizeof(query),
 1163             NULL, 0,
 1164             url, NULL);
 1165     pthread_mutex_unlock(&ctx->mutex);
 1166     if (error != 0){
 1167     errno = error;
 1168     return -1;
 1169     }
 1170     return 0;
 1171 }
 1172 
 1173 int smb_conn_utimes(struct smb_conn_ctx *ctx, const char *url, struct timeval *tbuf){
 1174     int                 error;
 1175     struct smb_conn_utimes_query    query;
 1176 
 1177     query.url_offs = sizeof(struct smb_conn_utimes_query);
 1178     memcpy(&query.tbuf, tbuf, 2 * sizeof(struct timeval));
 1179 
 1180     pthread_mutex_lock(&ctx->mutex);
 1181     ctx->access_time = time(NULL);
 1182     error = smb_conn_process_query(
 1183             ctx, UTIMES,
 1184             &query, sizeof(query),
 1185             NULL, 0,
 1186             url, NULL);
 1187     pthread_mutex_unlock(&ctx->mutex);
 1188     if (error != 0){
 1189     errno = error;
 1190     return -1;
 1191     }
 1192     return 0;
 1193 }
 1194 
 1195 int smb_conn_setxattr(struct smb_conn_ctx *ctx, const char *url, const char *name,
 1196                         const void *value, size_t size, int flags){
 1197 
 1198     int                 error;
 1199     struct smb_conn_setxattr_query  query;
 1200 
 1201     if (size > ctx->shmem_size){
 1202     errno = EINVAL;
 1203     return -1;
 1204     }
 1205 
 1206     query.url_offs  = sizeof(struct smb_conn_setxattr_query);
 1207     query.name_offs = sizeof(struct smb_conn_setxattr_query) + strlen(url) + 1;
 1208     query.bufsize   = size;
 1209     query.flags     = flags;
 1210 
 1211     pthread_mutex_lock(&ctx->mutex);
 1212     ctx->access_time = time(NULL);
 1213     memcpy(ctx->shmem_ptr, value, size);
 1214     msync(ctx->shmem_ptr, size, MS_SYNC);
 1215     error = smb_conn_process_query(
 1216             ctx, SETXATTR,
 1217             &query, sizeof(query),
 1218             NULL, 0,
 1219             url, name, NULL);
 1220     pthread_mutex_unlock(&ctx->mutex);
 1221     if (error != 0){
 1222     errno = error;
 1223     return -1;
 1224     }
 1225     return 0;
 1226 }
 1227 
 1228 int smb_conn_getxattr(struct smb_conn_ctx *ctx,
 1229             const char *url, const char *name,
 1230             void *value, size_t size){
 1231 
 1232     int                 error;
 1233     struct smb_conn_getxattr_query  query;
 1234     struct smb_conn_buf_reply       reply;
 1235 
 1236     if (size > ctx->shmem_size){
 1237     errno = EINVAL;
 1238     return -1;
 1239     }
 1240 
 1241     query.url_offs  = sizeof(struct smb_conn_getxattr_query);
 1242     query.name_offs = sizeof(struct smb_conn_getxattr_query) + strlen(url) + 1;
 1243     query.bufsize   = size;
 1244 
 1245     pthread_mutex_lock(&ctx->mutex);
 1246     ctx->access_time = time(NULL);
 1247     error = smb_conn_process_query(
 1248             ctx, GETXATTR,
 1249             &query, sizeof(query),
 1250             &reply, sizeof(reply),
 1251             url, name, NULL);
 1252     if ((error == 0) &&
 1253     (reply.bufsize > 0) && 
 1254     (reply.bufsize <= (ssize_t) size))
 1255             memcpy(value, ctx->shmem_ptr, reply.bufsize);
 1256     pthread_mutex_unlock(&ctx->mutex);
 1257     if (error != 0){
 1258     errno = error;
 1259     return -1;
 1260     }
 1261     if (((size == 0) && (reply.bufsize > (ssize_t) ctx->shmem_size)) ||
 1262     ((size >  0) && (reply.bufsize > (ssize_t) size))){
 1263     errno = EIO;
 1264     return -1;
 1265     }
 1266     return reply.bufsize;
 1267 }
 1268 
 1269 int smb_conn_listxattr(struct smb_conn_ctx *ctx,
 1270             const char *url,
 1271             char *list, size_t size){
 1272 
 1273     int                 error;
 1274     struct smb_conn_listxattr_query query;
 1275     struct smb_conn_buf_reply       reply;
 1276 
 1277     if (size > ctx->shmem_size){
 1278     errno = EINVAL;
 1279     return -1;
 1280     }
 1281 
 1282     query.url_offs = sizeof(struct smb_conn_listxattr_query);
 1283     query.bufsize  = size;
 1284 
 1285     pthread_mutex_lock(&ctx->mutex);
 1286     ctx->access_time = time(NULL);
 1287     error = smb_conn_process_query(
 1288             ctx, LISTXATTR,
 1289             &query, sizeof(query),
 1290             &reply, sizeof(reply),
 1291             url, NULL);
 1292     if ((error == 0) &&
 1293     (reply.bufsize > 0) &&
 1294     (reply.bufsize <= (ssize_t) size))
 1295             memcpy(list, ctx->shmem_ptr, reply.bufsize);
 1296     pthread_mutex_unlock(&ctx->mutex);
 1297     if (error != 0){
 1298     errno = error;
 1299     return -1;
 1300     }
 1301     if (((size == 0) && (reply.bufsize > (ssize_t) ctx->shmem_size)) ||
 1302     ((size >  0) && (reply.bufsize > (ssize_t) size))){
 1303     errno = EIO;
 1304     return -1;
 1305     }
 1306     return reply.bufsize;
 1307 }
 1308 
 1309 int smb_conn_removexattr(struct smb_conn_ctx *ctx,
 1310             const char *url, const char *name){
 1311 
 1312     int                 error;
 1313     struct smb_conn_removexattr_query   query;
 1314 
 1315     query.url_offs  = sizeof(struct smb_conn_removexattr_query);
 1316     query.name_offs = sizeof(struct smb_conn_removexattr_query) + strlen(url) + 1;
 1317 
 1318     pthread_mutex_lock(&ctx->mutex);
 1319     ctx->access_time = time(NULL);
 1320     error = smb_conn_process_query(
 1321             ctx, GETXATTR,
 1322             &query, sizeof(query),
 1323             NULL, 0,
 1324             url, name, NULL);
 1325     pthread_mutex_unlock(&ctx->mutex);
 1326     if (error != 0){
 1327     errno = error;
 1328     return -1;
 1329     }
 1330     return 0;
 1331 }