LCOV - code coverage report
Current view: top level - source4/client - cifsddio.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 138 182 75.8 %
Date: 2024-02-28 12:06:22 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /*
       2             :    CIFSDD - dd for SMB.
       3             :    IO routines, generic and specific.
       4             : 
       5             :    Copyright (C) James Peach 2005-2006
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "system/filesys.h"
      23             : #include "libcli/libcli.h"
      24             : #include "lib/cmdline/cmdline.h"
      25             : 
      26             : #include "cifsdd.h"
      27             : 
      28             : /* ------------------------------------------------------------------------- */
      29             : /* UNIX file descriptor IO.                                                  */
      30             : /* ------------------------------------------------------------------------- */
      31             : 
      32             : struct fd_handle
      33             : {
      34             :         struct dd_iohandle      h;
      35             :         int                     fd;
      36             : };
      37             : 
      38             : #define IO_HANDLE_TO_FD(h) (((struct fd_handle *)(h))->fd)
      39             : 
      40          15 : static bool fd_seek_func(void * handle, uint64_t offset)
      41             : {
      42             :         ssize_t ret;
      43             : 
      44          15 :         ret = lseek(IO_HANDLE_TO_FD(handle), offset, SEEK_SET);
      45          15 :         if (ret < 0) {
      46           0 :                 fprintf(stderr, "%s: seek failed: %s\n",
      47           0 :                                 PROGNAME, strerror(errno));
      48           0 :                 return(false);
      49             :         }
      50             : 
      51          15 :         return(true);
      52             : }
      53             : 
      54         236 : static bool fd_read_func(void * handle,
      55             :                         uint8_t * buf,
      56             :                         uint64_t wanted,
      57             :                         uint64_t * actual)
      58             : {
      59             :         ssize_t ret;
      60             : 
      61         236 :         ret = read(IO_HANDLE_TO_FD(handle), buf, wanted);
      62         236 :         if (ret < 0) {
      63           0 :                 fprintf(stderr, "%s: %llu byte read failed: %s\n",
      64             :                                 PROGNAME, (unsigned long long)wanted,
      65           0 :                                 strerror(errno));
      66           0 :                 return(false);
      67             :         }
      68             : 
      69         236 :         *actual = (uint64_t)ret;
      70         236 :         return(true);
      71             : }
      72             : 
      73         345 : static bool fd_write_func(void * handle,
      74             :                         uint8_t * buf,
      75             :                         uint64_t wanted,
      76             :                         uint64_t * actual)
      77             : {
      78             :         ssize_t ret;
      79             : 
      80         345 :         ret = write(IO_HANDLE_TO_FD(handle), buf, wanted);
      81         345 :         if (ret < 0) {
      82           0 :                 fprintf(stderr, "%s: %llu byte write failed: %s\n",
      83             :                                 PROGNAME, (unsigned long long)wanted,
      84           0 :                                 strerror(errno));
      85           0 :                 return(false);
      86             :         }
      87             : 
      88         345 :         *actual = (uint64_t)ret;
      89         345 :         return(true);
      90             : }
      91             : 
      92          15 : static struct dd_iohandle * open_fd_handle(const char * path,
      93             :                                         uint64_t io_size,
      94             :                                         int options)
      95             : {
      96             :         struct fd_handle * fdh;
      97          15 :         int oflags = 0;
      98             : 
      99          15 :         DEBUG(4, ("opening fd stream for %s\n", path));
     100          15 :         if ((fdh = talloc_zero(NULL, struct fd_handle)) == NULL) {
     101           0 :                 return(NULL);
     102             :         }
     103             : 
     104          15 :         fdh->h.io_read = fd_read_func;
     105          15 :         fdh->h.io_write = fd_write_func;
     106          15 :         fdh->h.io_seek = fd_seek_func;
     107             : 
     108          15 :         if (options & DD_DIRECT_IO) {
     109             : #ifdef HAVE_OPEN_O_DIRECT
     110           0 :                 oflags |= O_DIRECT;
     111             : #else
     112             :                 DEBUG(1, ("no support for direct IO on this platform\n"));
     113             : #endif
     114             :         }
     115             : 
     116          15 :         if (options & DD_SYNC_IO)
     117           0 :                 oflags |= O_SYNC;
     118             : 
     119          15 :         oflags |= (options & DD_WRITE) ?  (O_WRONLY | O_CREAT) : (O_RDONLY);
     120             : 
     121          15 :         fdh->fd = open(path, oflags, 0644);
     122          15 :         if (fdh->fd < 0) {
     123           0 :                 fprintf(stderr, "%s: %s: %s\n",
     124           0 :                         PROGNAME, path, strerror(errno));
     125           0 :                 talloc_free(fdh);
     126           0 :                 return(NULL);
     127             :         }
     128             : 
     129          15 :         if (options & DD_OPLOCK) {
     130           0 :                 DEBUG(2, ("FIXME: take local oplock on %s\n", path));
     131             :         }
     132             : 
     133             :         SMB_ASSERT((void *)fdh == (void *)&fdh->h);
     134          15 :         return(&fdh->h);
     135             : }
     136             : 
     137             : /* ------------------------------------------------------------------------- */
     138             : /* CIFS client IO.                                                           */
     139             : /* ------------------------------------------------------------------------- */
     140             : 
     141             : struct cifs_handle
     142             : {
     143             :         struct dd_iohandle      h;
     144             :         struct smbcli_state *   cli;
     145             :         int                     fnum;
     146             :         uint64_t                offset;
     147             : };
     148             : 
     149             : #define IO_HANDLE_TO_SMB(h) ((struct cifs_handle *)(h))
     150             : 
     151          21 : static bool smb_seek_func(void * handle, uint64_t offset)
     152             : {
     153          21 :         IO_HANDLE_TO_SMB(handle)->offset = offset;
     154          21 :         return(true);
     155             : }
     156             : 
     157         472 : static bool smb_read_func(void * handle, uint8_t * buf, uint64_t wanted,
     158             :                           uint64_t * actual)
     159             : {
     160             :         NTSTATUS                ret;
     161             :         union smb_read          r;
     162             :         struct cifs_handle *    smbh;
     163             : 
     164         472 :         ZERO_STRUCT(r);
     165         472 :         smbh = IO_HANDLE_TO_SMB(handle);
     166             : 
     167         472 :         r.generic.level         = RAW_READ_READX;
     168         472 :         r.readx.in.file.fnum    = smbh->fnum;
     169         472 :         r.readx.in.offset       = smbh->offset;
     170         472 :         r.readx.in.mincnt       = wanted;
     171         472 :         r.readx.in.maxcnt       = wanted;
     172         472 :         r.readx.out.data        = buf;
     173             : 
     174             :         /* FIXME: Should I really set readx.in.remaining? That just seems
     175             :          * redundant.
     176             :          */
     177         472 :         ret = smb_raw_read(smbh->cli->tree, &r);
     178         472 :         if (!NT_STATUS_IS_OK(ret)) {
     179           0 :                 fprintf(stderr, "%s: %llu byte read failed: %s\n",
     180             :                                 PROGNAME, (unsigned long long)wanted,
     181             :                                 nt_errstr(ret));
     182           0 :                 return(false);
     183             :         }
     184             : 
     185             :         /* Trap integer wrap. */
     186         472 :         SMB_ASSERT((smbh->offset + r.readx.out.nread) >= smbh->offset);
     187             : 
     188         472 :         *actual = r.readx.out.nread;
     189         472 :         smbh->offset += r.readx.out.nread;
     190         472 :         return(true);
     191             : }
     192             : 
     193         345 : static bool smb_write_func(void * handle, uint8_t * buf, uint64_t wanted,
     194             :                            uint64_t * actual)
     195             : {
     196             :         NTSTATUS                ret;
     197             :         union smb_write         w;
     198             :         struct cifs_handle *    smbh;
     199             : 
     200         345 :         ZERO_STRUCT(w);
     201         345 :         smbh = IO_HANDLE_TO_SMB(handle);
     202             : 
     203         345 :         w.generic.level         = RAW_WRITE_WRITEX;
     204         345 :         w.writex.in.file.fnum   = smbh->fnum;
     205         345 :         w.writex.in.offset      = smbh->offset;
     206         345 :         w.writex.in.count       = wanted;
     207         345 :         w.writex.in.data        = buf;
     208             : 
     209         345 :         ret = smb_raw_write(smbh->cli->tree, &w);
     210         345 :         if (!NT_STATUS_IS_OK(ret)) {
     211           0 :                 fprintf(stderr, "%s: %llu byte write failed: %s\n",
     212             :                                 PROGNAME, (unsigned long long)wanted,
     213             :                                 nt_errstr(ret));
     214           0 :                 return(false);
     215             :         }
     216             : 
     217         345 :         *actual = w.writex.out.nwritten;
     218         345 :         smbh->offset += w.writex.out.nwritten;
     219         345 :         return(true);
     220             : }
     221             : 
     222          21 : static struct smbcli_state * init_smb_session(struct resolve_context *resolve_ctx,
     223             :                                               struct tevent_context *ev,
     224             :                                               const char * host,
     225             :                                               const char **ports,
     226             :                                               const char * share,
     227             :                                               const char *socket_options,
     228             :                                               struct smbcli_options *options,
     229             :                                               struct smbcli_session_options *session_options,
     230             :                                               struct gensec_settings *gensec_settings)
     231             : {
     232             :         NTSTATUS                ret;
     233          21 :         struct smbcli_state *   cli = NULL;
     234             : 
     235             :         /* When we support SMB URLs, we can get different user credentials for
     236             :          * each connection, but for now, we just use the same one for both.
     237             :          */
     238          21 :         ret = smbcli_full_connection(NULL, &cli, host, ports, share,
     239             :                                 NULL /* devtype */,
     240             :                                 socket_options,
     241             :                                 samba_cmdline_get_creds(),
     242             :                                 resolve_ctx,
     243             :                                 ev, options,
     244             :                                 session_options,
     245             :                                 gensec_settings);
     246             : 
     247          21 :         if (!NT_STATUS_IS_OK(ret)) {
     248           0 :                 fprintf(stderr, "%s: connecting to //%s/%s: %s\n",
     249             :                         PROGNAME, host, share, nt_errstr(ret));
     250           0 :                 return(NULL);
     251             :         }
     252             : 
     253          21 :         return(cli);
     254             : }
     255             : 
     256          21 : static int open_smb_file(struct smbcli_state * cli,
     257             :                         const char * path,
     258             :                         int options)
     259             : {
     260             :         NTSTATUS        ret;
     261             :         union smb_open  o;
     262             : 
     263          21 :         ZERO_STRUCT(o);
     264             : 
     265          21 :         o.ntcreatex.level = RAW_OPEN_NTCREATEX;
     266          21 :         o.ntcreatex.in.fname = path;
     267             : 
     268             :         /* TODO: It's not clear whether to use these flags or to use the
     269             :          * similarly named NTCREATEX flags in the create_options field.
     270             :          */
     271          21 :         if (options & DD_DIRECT_IO)
     272           0 :                 o.ntcreatex.in.flags |= FILE_FLAG_NO_BUFFERING;
     273             : 
     274          21 :         if (options & DD_SYNC_IO)
     275           0 :                 o.ntcreatex.in.flags |= FILE_FLAG_WRITE_THROUGH;
     276             : 
     277          42 :         o.ntcreatex.in.access_mask |=
     278          21 :                 (options & DD_WRITE) ? SEC_FILE_WRITE_DATA
     279          21 :                                         : SEC_FILE_READ_DATA;
     280             : 
     281             :         /* Try to create the file only if we will be writing to it. */
     282          21 :         o.ntcreatex.in.open_disposition =
     283          21 :                 (options & DD_WRITE) ? NTCREATEX_DISP_OPEN_IF
     284          21 :                                         : NTCREATEX_DISP_OPEN;
     285             : 
     286          21 :         o.ntcreatex.in.share_access =
     287             :                 NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
     288             : 
     289          21 :         if (options & DD_OPLOCK) {
     290           0 :                 o.ntcreatex.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK;
     291             :         }
     292             : 
     293          21 :         ret = smb_raw_open(cli->tree, NULL, &o);
     294          21 :         if (!NT_STATUS_IS_OK(ret)) {
     295           0 :                 fprintf(stderr, "%s: opening %s: %s\n",
     296             :                         PROGNAME, path, nt_errstr(ret));
     297           0 :                 return(-1);
     298             :         }
     299             : 
     300          21 :         return(o.ntcreatex.out.file.fnum);
     301             : }
     302             : 
     303          21 : static struct dd_iohandle * open_cifs_handle(struct resolve_context *resolve_ctx,
     304             :                                              struct tevent_context *ev,
     305             :                                              const char * host,
     306             :                                         const char **ports,
     307             :                                         const char * share,
     308             :                                         const char * path,
     309             :                                         uint64_t io_size,
     310             :                                         int options,
     311             :                                         const char *socket_options,
     312             :                                         struct smbcli_options *smb_options,
     313             :                                         struct smbcli_session_options *smb_session_options,
     314             :                                         struct gensec_settings *gensec_settings)
     315             : {
     316             :         struct cifs_handle * smbh;
     317             : 
     318          21 :         if (path == NULL  || *path == '\0') {
     319           0 :                 fprintf(stderr, "%s: missing path name within share //%s/%s\n",
     320             :                         PROGNAME, host, share);
     321             :         }
     322             : 
     323          21 :         DEBUG(4, ("opening SMB stream to //%s/%s for %s\n",
     324             :                 host, share, path));
     325             : 
     326          21 :         if ((smbh = talloc_zero(NULL, struct cifs_handle)) == NULL) {
     327           0 :                 return(NULL);
     328             :         }
     329             : 
     330          21 :         smbh->h.io_read = smb_read_func;
     331          21 :         smbh->h.io_write = smb_write_func;
     332          21 :         smbh->h.io_seek = smb_seek_func;
     333             : 
     334          21 :         if ((smbh->cli = init_smb_session(resolve_ctx, ev, host, ports, share,
     335             :                                           socket_options,
     336             :                                           smb_options, smb_session_options,
     337             :                                           gensec_settings)) == NULL) {
     338           0 :                 return(NULL);
     339             :         }
     340             : 
     341          21 :         DEBUG(4, ("connected to //%s/%s with xmit size of %u bytes\n",
     342             :                 host, share, smbh->cli->transport->negotiate.max_xmit));
     343             : 
     344          21 :         smbh->fnum = open_smb_file(smbh->cli, path, options);
     345          21 :         return(&smbh->h);
     346             : }
     347             : 
     348             : /* ------------------------------------------------------------------------- */
     349             : /* Abstract IO interface.                                                    */
     350             : /* ------------------------------------------------------------------------- */
     351             : 
     352          36 : struct dd_iohandle * dd_open_path(struct resolve_context *resolve_ctx, 
     353             :                                   struct tevent_context *ev,
     354             :                                   const char * path,
     355             :                                   const char **ports,
     356             :                                 uint64_t io_size,
     357             :                                 int options,
     358             :                                 const char *socket_options,
     359             :                                 struct smbcli_options *smb_options,
     360             :                                 struct smbcli_session_options *smb_session_options,
     361             :                                 struct gensec_settings *gensec_settings)
     362             : {
     363          36 :         if (file_exist(path)) {
     364          14 :                 return(open_fd_handle(path, io_size, options));
     365             :         } else {
     366             :                 char * host;
     367             :                 char * share;
     368             : 
     369          22 :                 if (smbcli_parse_unc(path, NULL, &host, &share)) {
     370             :                         const char * remain;
     371          21 :                         remain = strstr(path, share) + strlen(share);
     372             : 
     373             :                         /* Skip over leading directory separators. */
     374          42 :                         while (*remain == '/' || *remain == '\\') { remain++; }
     375             : 
     376          21 :                         return(open_cifs_handle(resolve_ctx, ev, host, ports,
     377             :                                                 share, remain,
     378             :                                                 io_size, options, 
     379             :                                                 socket_options, smb_options,
     380             :                                                 smb_session_options,
     381             :                                                 gensec_settings));
     382             :                 }
     383             : 
     384           1 :                 return(open_fd_handle(path, io_size, options));
     385             :         }
     386             : }
     387             : 
     388             : /* Fill the buffer till it has at least need_size bytes. Use read operations of
     389             :  * block_size bytes. Return the number of bytes read and fill buf_size with
     390             :  * the new buffer size.
     391             :  *
     392             :  * NOTE: The IO buffer is guaranteed to be big enough to fit
     393             :  * need_size + block_size bytes into it.
     394             :  */
     395         696 : bool dd_fill_block(struct dd_iohandle * h,
     396             :                 uint8_t * buf,
     397             :                 uint64_t * buf_size,
     398             :                 uint64_t need_size,
     399             :                 uint64_t block_size)
     400             : {
     401             :         uint64_t read_size;
     402             : 
     403         696 :         SMB_ASSERT(block_size > 0);
     404         696 :         SMB_ASSERT(need_size > 0);
     405             : 
     406        1386 :         while (*buf_size < need_size) {
     407             : 
     408         708 :                 if (!h->io_read(h, buf + (*buf_size), block_size, &read_size)) {
     409           0 :                         return(false);
     410             :                 }
     411             : 
     412         708 :                 if (read_size == 0) {
     413          18 :                         h->io_flags |= DD_END_OF_FILE;
     414          18 :                         break;
     415             :                 }
     416             : 
     417         690 :                 DEBUG(6, ("added %llu bytes to IO buffer (need %llu bytes)\n",
     418             :                         (unsigned long long)read_size,
     419             :                         (unsigned long long)need_size));
     420             : 
     421         690 :                 *buf_size += read_size;
     422         690 :                 dd_stats.in.bytes += read_size;
     423             : 
     424         690 :                 if (read_size == block_size) {
     425         678 :                         dd_stats.in.fblocks++;
     426             :                 } else {
     427          12 :                         DEBUG(3, ("partial read of %llu bytes (expected %llu)\n",
     428             :                                 (unsigned long long)read_size,
     429             :                                 (unsigned long long)block_size));
     430          12 :                         dd_stats.in.pblocks++;
     431             :                 }
     432             :         }
     433             : 
     434         696 :         return(true);
     435             : }
     436             : 
     437             : /* Flush a buffer that contains buf_size bytes. Use writes of block_size to do it,
     438             :  * and shift any remaining bytes back to the head of the buffer when there are
     439             :  * no more block_size sized IOs left.
     440             :  */
     441         690 : bool dd_flush_block(struct dd_iohandle * h,
     442             :                 uint8_t * buf,
     443             :                 uint64_t * buf_size,
     444             :                 uint64_t block_size)
     445             : {
     446             :         uint64_t write_size;
     447         690 :         uint64_t total_size = 0;
     448             : 
     449         690 :         SMB_ASSERT(block_size > 0);
     450             : 
     451             :         /* We have explicitly been asked to write a partial block. */
     452         690 :         if ((*buf_size) < block_size) {
     453             : 
     454          12 :                 if (!h->io_write(h, buf, *buf_size, &write_size)) {
     455           0 :                         return(false);
     456             :                 }
     457             : 
     458          12 :                 if (write_size == 0) {
     459           0 :                         fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
     460             :                                         PROGNAME);
     461           0 :                         return(false);
     462             :                 }
     463             : 
     464          12 :                 total_size += write_size;
     465          12 :                 dd_stats.out.bytes += write_size;
     466          12 :                 dd_stats.out.pblocks++;
     467             :         }
     468             : 
     469             :         /* Write as many full blocks as there are in the buffer. */
     470        1368 :         while (((*buf_size) - total_size) >= block_size) {
     471             : 
     472         678 :                 if (!h->io_write(h, buf + total_size, block_size, &write_size)) {
     473           0 :                         return(false);
     474             :                 }
     475             : 
     476         678 :                 if (write_size == 0) {
     477           0 :                         fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
     478             :                                         PROGNAME);
     479           0 :                         return(false);
     480             :                 }
     481             : 
     482         678 :                 if (write_size == block_size) {
     483         678 :                         dd_stats.out.fblocks++;
     484             :                 } else {
     485           0 :                         dd_stats.out.pblocks++;
     486             :                 }
     487             : 
     488         678 :                 total_size += write_size;
     489         678 :                 dd_stats.out.bytes += write_size;
     490             : 
     491         678 :                 DEBUG(6, ("flushed %llu bytes from IO buffer of %llu bytes (%llu remain)\n",
     492             :                         (unsigned long long)block_size,
     493             :                         (unsigned long long)block_size,
     494             :                         (unsigned long long)(block_size - total_size)));
     495             :         }
     496             : 
     497         690 :         SMB_ASSERT(total_size > 0);
     498             : 
     499             :         /* We have flushed as much of the IO buffer as we can while
     500             :          * still doing block_size'd operations. Shift any remaining data
     501             :          * to the front of the IO buffer.
     502             :          */
     503         690 :         if ((*buf_size) > total_size) {
     504           0 :                 uint64_t remain = (*buf_size) - total_size;
     505             : 
     506           0 :                 DEBUG(3, ("shifting %llu remainder bytes to IO buffer head\n",
     507             :                         (unsigned long long)remain));
     508             : 
     509           0 :                 memmove(buf, buf + total_size, remain);
     510           0 :                 (*buf_size) = remain;
     511         690 :         } else if ((*buf_size) == total_size) {
     512         690 :                 (*buf_size) = 0;
     513             :         } else {
     514             :                 /* Else buffer contains buf_size bytes that we will append
     515             :                  * to next time round.
     516             :                  */
     517           0 :                 DEBUG(3, ("%llu unflushed bytes left in IO buffer\n",
     518             :                         (unsigned long long)(*buf_size)));
     519             :         }
     520             : 
     521         690 :         return(true);
     522             : }
     523             : 
     524             : /* vim: set sw=8 sts=8 ts=8 tw=79 : */

Generated by: LCOV version 1.14