lib: Use asn1_has_error()
[sfrench/samba-autobuild/.git] / source3 / libsmb / libsmb_file.c
index 58403dfbb3681c6a0a8831f923b16c8cbe5a036c..6b436768ecbe4e640a27ec38a59afcb23692f1ab 100644 (file)
@@ -1,10 +1,10 @@
-/* 
+/*
    Unix SMB/Netbios implementation.
    SMB client library implementation
    Copyright (C) Andrew Tridgell 1998
    Copyright (C) Richard Sharpe 2000, 2002
    Copyright (C) John Terpstra 2000
-   Copyright (C) Tom Jansen (Ninja ISD) 2002 
+   Copyright (C) Tom Jansen (Ninja ISD) 2002
    Copyright (C) Derrell Lipman 2003-2008
    Copyright (C) Jeremy Allison 2007, 2008
 
 */
 
 #include "includes.h"
+#include "libsmb/libsmb.h"
 #include "libsmbclient.h"
 #include "libsmb_internal.h"
-
+#include "../libcli/smb/smbXcli_base.h"
 
 /*
  * Routine to open() a file ...
@@ -48,6 +49,7 @@ SMBC_open_ctx(SMBCCTX *context,
        SMBCSRV *srv   = NULL;
        SMBCFILE *file = NULL;
        uint16_t fd;
+       uint16_t port = 0;
        NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID;
        TALLOC_CTX *frame = talloc_stackframe();
 
@@ -68,6 +70,7 @@ SMBC_open_ctx(SMBCCTX *context,
                             fname,
                             &workgroup,
                             &server,
+                            &port,
                             &share,
                             &path,
                             &user,
@@ -88,7 +91,7 @@ SMBC_open_ctx(SMBCCTX *context,
        }
 
        srv = SMBC_server(frame, context, True,
-                          server, share, &workgroup, &user, &password);
+                          server, port, share, &workgroup, &user, &password);
        if (!srv) {
                if (errno == EPERM) errno = EACCES;
                TALLOC_FREE(frame);
@@ -110,9 +113,10 @@ SMBC_open_ctx(SMBCCTX *context,
                ZERO_STRUCTP(file);
 
                /*d_printf(">>>open: resolving %s\n", path);*/
-               if (!cli_resolve_path(frame, "", context->internal->auth_info,
-                               srv->cli, path,
-                               &targetcli, &targetpath)) {
+               status = cli_resolve_path(
+                       frame, "", context->internal->auth_info,
+                       srv->cli, path, &targetcli, &targetpath);
+               if (!NT_STATUS_IS_OK(status)) {
                        d_printf("Could not resolve %s\n", path);
                         errno = ENOENT;
                        SAFE_FREE(file);
@@ -140,6 +144,14 @@ SMBC_open_ctx(SMBCCTX *context,
                file->srv     = srv;
                file->offset  = 0;
                file->file    = True;
+               /*
+                * targetcli is either equal to srv->cli or
+                * is a subsidiary DFS connection. Either way
+                * file->cli_fd belongs to it so we must cache
+                * it for read/write/close, not re-resolve each time.
+                * Re-resolving is both slow and incorrect.
+                */
+               file->targetcli = targetcli;
 
                DLIST_ADD(context->internal->files, file);
 
@@ -196,7 +208,7 @@ SMBC_open_ctx(SMBCCTX *context,
 }
 
 /*
- * Routine to create a file 
+ * Routine to create a file
  */
 
 SMBCFILE *
@@ -223,12 +235,9 @@ SMBC_read_ctx(SMBCCTX *context,
               void *buf,
               size_t count)
 {
-       int ret;
-       char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
-       char *path = NULL;
-       char *targetpath = NULL;
-       struct cli_state *targetcli = NULL;
+       size_t ret;
        TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
 
         /*
          * offset:
@@ -265,47 +274,71 @@ SMBC_read_ctx(SMBCCTX *context,
                return -1;
        }
 
-       /*d_printf(">>>read: parsing %s\n", file->fname);*/
-       if (SMBC_parse_path(frame,
-                            context,
-                            file->fname,
-                            NULL,
-                            &server,
-                            &share,
-                            &path,
-                            &user,
-                            &password,
-                            NULL)) {
-                errno = EINVAL;
+       status = cli_read(file->targetcli, file->cli_fd, (char *)buf, offset,
+                         count, &ret);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = SMBC_errno(context, file->targetcli);
                TALLOC_FREE(frame);
-                return -1;
-        }
+               return -1;
+       }
 
-       /*d_printf(">>>read: resolving %s\n", path);*/
-       if (!cli_resolve_path(frame, "", context->internal->auth_info,
-                       file->srv->cli, path,
-                       &targetcli, &targetpath)) {
-               d_printf("Could not resolve %s\n", path);
-                errno = ENOENT;
+       file->offset += ret;
+
+       DEBUG(4, ("  --> %ld\n", (unsigned long)ret));
+
+       TALLOC_FREE(frame);
+       return ret;  /* Success, ret bytes of data ... */
+}
+
+off_t
+SMBC_splice_ctx(SMBCCTX *context,
+                SMBCFILE *srcfile,
+                SMBCFILE *dstfile,
+                off_t count,
+                int (*splice_cb)(off_t n, void *priv),
+                void *priv)
+{
+       off_t written;
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
+
+       if (!context || !context->internal->initialized) {
+               errno = EINVAL;
                TALLOC_FREE(frame);
                return -1;
        }
-       /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
 
-       ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
+       if (!srcfile ||
+           !SMBC_dlist_contains(context->internal->files, srcfile))
+       {
+               errno = EBADF;
+               TALLOC_FREE(frame);
+               return -1;
+       }
 
-       if (ret < 0) {
-               errno = SMBC_errno(context, targetcli);
+       if (!dstfile ||
+           !SMBC_dlist_contains(context->internal->files, dstfile))
+       {
+               errno = EBADF;
                TALLOC_FREE(frame);
                return -1;
        }
 
-       file->offset += ret;
+       status = cli_splice(srcfile->targetcli, dstfile->targetcli,
+                           srcfile->cli_fd, dstfile->cli_fd,
+                           count, srcfile->offset, dstfile->offset, &written,
+                           splice_cb, priv);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = SMBC_errno(context, srcfile->targetcli);
+               TALLOC_FREE(frame);
+               return -1;
+       }
 
-       DEBUG(4, ("  --> %d\n", ret));
+       srcfile->offset += written;
+       dstfile->offset += written;
 
        TALLOC_FREE(frame);
-       return ret;  /* Success, ret bytes of data ... */
+       return written;
 }
 
 /*
@@ -318,13 +351,9 @@ SMBC_write_ctx(SMBCCTX *context,
                const void *buf,
                size_t count)
 {
-       int ret;
         off_t offset;
-       char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
-       char *path = NULL;
-       char *targetpath = NULL;
-       struct cli_state *targetcli = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
 
        /* First check all pointers before dereferencing them */
 
@@ -350,45 +379,18 @@ SMBC_write_ctx(SMBCCTX *context,
 
         offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
 
-       /*d_printf(">>>write: parsing %s\n", file->fname);*/
-       if (SMBC_parse_path(frame,
-                            context,
-                            file->fname,
-                            NULL,
-                            &server,
-                            &share,
-                            &path,
-                            &user,
-                            &password,
-                            NULL)) {
-                errno = EINVAL;
-               TALLOC_FREE(frame);
-                return -1;
-        }
-
-       /*d_printf(">>>write: resolving %s\n", path);*/
-       if (!cli_resolve_path(frame, "", context->internal->auth_info,
-                       file->srv->cli, path,
-                       &targetcli, &targetpath)) {
-               d_printf("Could not resolve %s\n", path);
-                errno = ENOENT;
-               TALLOC_FREE(frame);
-               return -1;
-       }
-       /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
-
-       ret = cli_write(targetcli, file->cli_fd,
-                        0, (char *)buf, offset, count);
-       if (ret <= 0) {
-               errno = SMBC_errno(context, targetcli);
+       status = cli_writeall(file->targetcli, file->cli_fd,
+                             0, (const uint8_t *)buf, offset, count, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = map_errno_from_nt_status(status);
                TALLOC_FREE(frame);
                return -1;
        }
 
-       file->offset += ret;
+       file->offset += count;
 
        TALLOC_FREE(frame);
-       return ret;  /* Success, 0 bytes of data ... */
+       return count;  /* Success, 0 bytes of data ... */
 }
 
 /*
@@ -399,11 +401,6 @@ int
 SMBC_close_ctx(SMBCCTX *context,
                SMBCFILE *file)
 {
-        SMBCSRV *srv;
-       char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
-       char *path = NULL;
-       char *targetpath = NULL;
-       struct cli_state *targetcli = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (!context || !context->internal->initialized) {
@@ -424,39 +421,13 @@ SMBC_close_ctx(SMBCCTX *context,
                return smbc_getFunctionClosedir(context)(context, file);
        }
 
-       /*d_printf(">>>close: parsing %s\n", file->fname);*/
-       if (SMBC_parse_path(frame,
-                            context,
-                            file->fname,
-                            NULL,
-                            &server,
-                            &share,
-                            &path,
-                            &user,
-                            &password,
-                            NULL)) {
-                errno = EINVAL;
-               TALLOC_FREE(frame);
-                return -1;
-        }
-
-       /*d_printf(">>>close: resolving %s\n", path);*/
-       if (!cli_resolve_path(frame, "", context->internal->auth_info,
-                       file->srv->cli, path,
-                       &targetcli, &targetpath)) {
-               d_printf("Could not resolve %s\n", path);
-                errno = ENOENT;
-               TALLOC_FREE(frame);
-               return -1;
-       }
-       /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
-
-       if (!NT_STATUS_IS_OK(cli_close(targetcli, file->cli_fd))) {
-               DEBUG(3, ("cli_close failed on %s. purging server.\n", 
+       if (!NT_STATUS_IS_OK(cli_close(file->targetcli, file->cli_fd))) {
+               SMBCSRV *srv;
+               DEBUG(3, ("cli_close failed on %s. purging server.\n",
                          file->fname));
-               /* Deallocate slot and remove the server 
+               /* Deallocate slot and remove the server
                 * from the server cache if unused */
-               errno = SMBC_errno(context, targetcli);
+               errno = SMBC_errno(context, file->targetcli);
                srv = file->srv;
                DLIST_REMOVE(context->internal->files, file);
                SAFE_FREE(file->fname);
@@ -480,9 +451,9 @@ SMBC_close_ctx(SMBCCTX *context,
 bool
 SMBC_getatr(SMBCCTX * context,
             SMBCSRV *srv,
-            char *path,
-            uint16 *mode,
-            SMB_OFF_T *size,
+            const char *path,
+            uint16_t *mode,
+            off_t *size,
             struct timespec *create_time_ts,
             struct timespec *access_time_ts,
             struct timespec *write_time_ts,
@@ -494,6 +465,7 @@ SMBC_getatr(SMBCCTX * context,
        struct cli_state *targetcli = NULL;
        time_t write_time;
        TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
 
        if (!context || !context->internal->initialized) {
                errno = EINVAL;
@@ -521,9 +493,10 @@ SMBC_getatr(SMBCCTX * context,
        }
        DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
 
-       if (!cli_resolve_path(frame, "", context->internal->auth_info,
-                       srv->cli, fixedpath,
-                       &targetcli, &targetpath)) {
+       status = cli_resolve_path(frame, "", context->internal->auth_info,
+                                 srv->cli, fixedpath,
+                                 &targetcli, &targetpath);
+       if (!NT_STATUS_IS_OK(status)) {
                d_printf("Couldn't resolve %s\n", path);
                 errno = ENOENT;
                TALLOC_FREE(frame);
@@ -541,11 +514,24 @@ SMBC_getatr(SMBCCTX * context,
                return True;
         }
 
-       /* if this is NT then don't bother with the getatr */
-       if (targetcli->capabilities & CAP_NT_SMBS) {
-                errno = EPERM;
+       srv->no_pathinfo2 = True;
+
+       if (!srv->no_pathinfo3 &&
+            NT_STATUS_IS_OK(cli_qpathinfo3(targetcli, targetpath,
+                           create_time_ts,
+                           access_time_ts,
+                           write_time_ts,
+                           change_time_ts,
+                          size, mode, ino))) {
                TALLOC_FREE(frame);
-                return False;
+               return True;
+        }
+
+       srv->no_pathinfo3 = True;
+
+       /* if this is NT then don't bother with the getatr */
+       if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
+               goto all_failed;
         }
 
        if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
@@ -564,11 +550,17 @@ SMBC_getatr(SMBCCTX * context,
                 if (change_time_ts != NULL) {
                         *change_time_ts = w_time_ts;
                 }
-               srv->no_pathinfo2 = True;
+               if (ino) {
+                       *ino = 0;
+               }
                TALLOC_FREE(frame);
                return True;
        }
 
+all_failed:
+       srv->no_pathinfo2 = False;
+       srv->no_pathinfo3 = False;
+
         errno = EPERM;
        TALLOC_FREE(frame);
        return False;
@@ -585,12 +577,12 @@ SMBC_getatr(SMBCCTX * context,
  * "mode" (attributes) parameter may be set to -1 if it is not to be set.
  */
 bool
-SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
+SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
             time_t create_time,
             time_t access_time,
             time_t write_time,
             time_t change_time,
-            uint16 mode)
+            uint16_t mode)
 {
         uint16_t fd;
         int ret;
@@ -611,7 +603,7 @@ SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
                                                   mode))) {
 
                 /*
-                 * setpathinfo is not supported; go to plan B. 
+                 * setpathinfo is not supported; go to plan B.
                  *
                  * cli_setatr() does not work on win98, and it also doesn't
                  * support setting the access time (only the modification
@@ -645,7 +637,7 @@ SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
                  * cli_setatr() for that, and with only this parameter, it
                  * seems to work on win98.
                  */
-                if (ret && mode != (uint16) -1) {
+                if (ret && mode != (uint16_t) -1) {
                         ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0));
                 }
 
@@ -670,11 +662,7 @@ SMBC_lseek_ctx(SMBCCTX *context,
                off_t offset,
                int whence)
 {
-       SMB_OFF_T size;
-       char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
-       char *path = NULL;
-       char *targetpath = NULL;
-       struct cli_state *targetcli = NULL;
+       off_t size;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (!context || !context->internal->initialized) {
@@ -703,39 +691,12 @@ SMBC_lseek_ctx(SMBCCTX *context,
                file->offset += offset;
                break;
        case SEEK_END:
-               /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
-               if (SMBC_parse_path(frame,
-                                    context,
-                                    file->fname,
-                                    NULL,
-                                    &server,
-                                    &share,
-                                    &path,
-                                    &user,
-                                    &password,
-                                    NULL)) {
-                       errno = EINVAL;
-                       TALLOC_FREE(frame);
-                       return -1;
-               }
-
-               /*d_printf(">>>lseek: resolving %s\n", path);*/
-               if (!cli_resolve_path(frame, "", context->internal->auth_info,
-                               file->srv->cli, path,
-                               &targetcli, &targetpath)) {
-                       d_printf("Could not resolve %s\n", path);
-                        errno = ENOENT;
-                       TALLOC_FREE(frame);
-                       return -1;
-               }
-
-               /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
                if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
-                                            targetcli, file->cli_fd, NULL,
+                                            file->targetcli, file->cli_fd, NULL,
                                             &size, NULL, NULL, NULL, NULL,
                                             NULL))) {
-                        SMB_OFF_T b_size = size;
-                       if (!NT_STATUS_IS_OK(cli_getattrE(targetcli, file->cli_fd,
+                        off_t b_size = size;
+                       if (!NT_STATUS_IS_OK(cli_getattrE(file->targetcli, file->cli_fd,
                                           NULL, &b_size, NULL, NULL, NULL))) {
                                 errno = EINVAL;
                                 TALLOC_FREE(frame);
@@ -764,14 +725,7 @@ SMBC_ftruncate_ctx(SMBCCTX *context,
                    SMBCFILE *file,
                    off_t length)
 {
-       SMB_OFF_T size = length;
-       char *server = NULL;
-       char *share = NULL;
-       char *user = NULL;
-       char *password = NULL;
-       char *path = NULL;
-        char *targetpath = NULL;
-       struct cli_state *targetcli = NULL;
+       off_t size = length;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (!context || !context->internal->initialized) {
@@ -792,34 +746,7 @@ SMBC_ftruncate_ctx(SMBCCTX *context,
                return -1;
        }
 
-       /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
-       if (SMBC_parse_path(frame,
-                            context,
-                            file->fname,
-                            NULL,
-                            &server,
-                            &share,
-                            &path,
-                            &user,
-                            &password,
-                            NULL)) {
-                errno = EINVAL;
-               TALLOC_FREE(frame);
-                return -1;
-        }
-
-       /*d_printf(">>>fstat: resolving %s\n", path);*/
-       if (!cli_resolve_path(frame, "", context->internal->auth_info,
-                       file->srv->cli, path,
-                       &targetcli, &targetpath)) {
-               d_printf("Could not resolve %s\n", path);
-                errno = ENOENT;
-               TALLOC_FREE(frame);
-               return -1;
-       }
-       /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
-
-        if (!NT_STATUS_IS_OK(cli_ftruncate(targetcli, file->cli_fd, (uint64_t)size))) {
+        if (!NT_STATUS_IS_OK(cli_ftruncate(file->targetcli, file->cli_fd, (uint64_t)size))) {
                 errno = EINVAL;
                 TALLOC_FREE(frame);
                 return -1;