LCOV - code coverage report
Current view: top level - source4/ntvfs/posix - pvfs_streams.c (source / functions) Hit Total Coverage
Test: coverage report for vadcx-master-patch-75612 fe003de8 Lines: 209 285 73.3 %
Date: 2024-02-29 22:57:05 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    POSIX NTVFS backend - alternate data streams
       5             : 
       6             :    Copyright (C) Andrew Tridgell 2004
       7             : 
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             :    
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             :    
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "vfs_posix.h"
      24             : #include "librpc/gen_ndr/xattr.h"
      25             : 
      26             : /*
      27             :   normalise a stream name, removing a :$DATA suffix if there is one 
      28             :   Note: this returns the existing pointer to the name if the name does 
      29             :         not need normalising
      30             :  */
      31          61 : static const char *stream_name_normalise(TALLOC_CTX *ctx, const char *name)
      32             : {
      33          61 :         const char *c = strchr_m(name, ':');
      34          61 :         if (c == NULL || strcasecmp_m(c, ":$DATA") != 0) {
      35          51 :                 return name;
      36             :         }
      37          10 :         return talloc_strndup(ctx, name, c-name);
      38             : }
      39             : 
      40             : /*
      41             :   compare two stream names, taking account of the default $DATA extension
      42             :  */
      43         452 : static int stream_name_cmp(const char *name1, const char *name2)
      44             : {
      45           0 :         const char *c1, *c2;
      46           0 :         int l1, l2, ret;
      47         452 :         c1 = strchr_m(name1, ':');
      48         452 :         c2 = strchr_m(name2, ':');
      49             :         
      50             :         /* check the first part is the same */
      51         452 :         l1 = c1?(c1 - name1):strlen(name1);
      52         452 :         l2 = c2?(c2 - name2):strlen(name2);
      53         452 :         if (l1 != l2) {
      54         136 :                 return l1 - l2;
      55             :         }
      56         316 :         ret = strncasecmp_m(name1, name2, l1);
      57         316 :         if (ret != 0) {
      58          24 :                 return ret;
      59             :         }
      60             : 
      61             :         /* the first parts are the same, check the suffix */
      62         292 :         if (c1 && c2) {
      63           0 :                 return strcasecmp_m(c1, c2);
      64             :         }
      65             : 
      66         292 :         if (c1) {
      67           0 :                 return strcasecmp_m(c1, ":$DATA");
      68             :         }
      69         292 :         if (c2) {
      70           0 :                 return strcasecmp_m(c2, ":$DATA");
      71             :         }
      72             : 
      73             :         /* neither names have a suffix */
      74         292 :         return 0;
      75             : }
      76             : 
      77             : 
      78             : /*
      79             :   return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
      80             : */
      81          42 : NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs, 
      82             :                                  TALLOC_CTX *mem_ctx,
      83             :                                  struct pvfs_filename *name, int fd, 
      84             :                                  struct stream_information *info)
      85             : {
      86           0 :         struct xattr_DosStreams *streams;
      87           0 :         int i;
      88           0 :         NTSTATUS status;
      89             : 
      90             :         /* directories don't have streams */
      91          42 :         if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
      92           2 :                 info->num_streams = 0;
      93           2 :                 info->streams = NULL;
      94           2 :                 return NT_STATUS_OK;
      95             :         }
      96             : 
      97          40 :         streams = talloc(mem_ctx, struct xattr_DosStreams);
      98          40 :         if (streams == NULL) {
      99           0 :                 return NT_STATUS_NO_MEMORY;
     100             :         }
     101             : 
     102          40 :         status = pvfs_streams_load(pvfs, name, fd, streams);
     103          40 :         if (!NT_STATUS_IS_OK(status)) {
     104           0 :                 ZERO_STRUCTP(streams);
     105             :         }
     106             : 
     107          40 :         info->num_streams = streams->num_streams+1;
     108          40 :         info->streams = talloc_array(mem_ctx, struct stream_struct, info->num_streams);
     109          40 :         if (!info->streams) {
     110           0 :                 return NT_STATUS_NO_MEMORY;
     111             :         }
     112             : 
     113          40 :         info->streams[0].size          = name->st.st_size;
     114          40 :         info->streams[0].alloc_size    = name->dos.alloc_size;
     115          40 :         info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA");
     116             : 
     117          88 :         for (i=0;i<streams->num_streams;i++) {
     118          48 :                 info->streams[i+1].size          = streams->streams[i].size;
     119          48 :                 info->streams[i+1].alloc_size    = streams->streams[i].alloc_size;
     120          48 :                 if (strchr(streams->streams[i].name, ':') == NULL) {
     121          48 :                         info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams, 
     122             :                                                                            ":%s:$DATA",
     123          48 :                                                                            streams->streams[i].name);
     124             :                 } else {
     125           0 :                         info->streams[i+1].stream_name.s = talloc_strdup(streams->streams, 
     126           0 :                                                                          streams->streams[i].name);
     127             :                 }
     128             :         }
     129             : 
     130          40 :         return NT_STATUS_OK;
     131             : }
     132             : 
     133             : 
     134             : /*
     135             :   fill in the stream information for a name
     136             : */
     137      806402 : NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
     138             : {
     139           0 :         struct xattr_DosStreams *streams;
     140           0 :         int i;
     141           0 :         NTSTATUS status;
     142             : 
     143             :         /* the NULL stream always exists */
     144      806402 :         if (name->stream_name == NULL) {
     145      805873 :                 name->stream_exists = true;
     146      805873 :                 return NT_STATUS_OK;
     147             :         }
     148             : 
     149         529 :         streams = talloc(name, struct xattr_DosStreams);
     150         529 :         if (streams == NULL) {
     151           0 :                 return NT_STATUS_NO_MEMORY;
     152             :         }
     153             : 
     154         529 :         status = pvfs_streams_load(pvfs, name, fd, streams);
     155         529 :         if (!NT_STATUS_IS_OK(status)) {
     156           0 :                 talloc_free(streams);
     157           0 :                 return status;
     158             :         }
     159             : 
     160         627 :         for (i=0;i<streams->num_streams;i++) {
     161         342 :                 struct xattr_DosStream *s = &streams->streams[i];
     162         342 :                 if (stream_name_cmp(s->name, name->stream_name) == 0) {
     163         244 :                         name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size);
     164         244 :                         name->st.st_size     = s->size;
     165         244 :                         name->stream_exists = true;
     166         244 :                         talloc_free(streams);
     167         244 :                         return NT_STATUS_OK;
     168             :                 }
     169             :         }
     170             : 
     171         285 :         talloc_free(streams);
     172             : 
     173         285 :         name->dos.alloc_size = 0;
     174         285 :         name->st.st_size     = 0;
     175         285 :         name->stream_exists = false;
     176             : 
     177         285 :         return NT_STATUS_OK;
     178             : }
     179             : 
     180             : 
     181             : /*
     182             :   update size information for a stream
     183             : */
     184          69 : static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
     185             :                                         off_t size)
     186             : {
     187           0 :         struct xattr_DosStreams *streams;
     188           0 :         int i;
     189           0 :         NTSTATUS status;
     190             : 
     191          69 :         streams = talloc(name, struct xattr_DosStreams);
     192          69 :         if (streams == NULL) {
     193           0 :                 return NT_STATUS_NO_MEMORY;
     194             :         }
     195             : 
     196          69 :         status = pvfs_streams_load(pvfs, name, fd, streams);
     197          69 :         if (!NT_STATUS_IS_OK(status)) {
     198           0 :                 ZERO_STRUCTP(streams);
     199             :         }
     200             : 
     201          98 :         for (i=0;i<streams->num_streams;i++) {
     202          49 :                 struct xattr_DosStream *s = &streams->streams[i];
     203          49 :                 if (stream_name_cmp(s->name, name->stream_name) == 0) {
     204          20 :                         s->size       = size;
     205          20 :                         s->alloc_size = pvfs_round_alloc_size(pvfs, size);
     206          20 :                         break;
     207             :                 }
     208             :         }
     209             : 
     210          69 :         if (i == streams->num_streams) {
     211           0 :                 struct xattr_DosStream *s;
     212          49 :                 streams->streams = talloc_realloc(streams, streams->streams, 
     213             :                                                     struct xattr_DosStream,
     214             :                                                     streams->num_streams+1);
     215          49 :                 if (streams->streams == NULL) {
     216           0 :                         talloc_free(streams);
     217           0 :                         return NT_STATUS_NO_MEMORY;
     218             :                 }
     219          49 :                 streams->num_streams++;
     220          49 :                 s = &streams->streams[i];
     221             :                 
     222          49 :                 s->flags      = XATTR_STREAM_FLAG_INTERNAL;
     223          49 :                 s->size       = size;
     224          49 :                 s->alloc_size = pvfs_round_alloc_size(pvfs, size);
     225          49 :                 s->name       = stream_name_normalise(streams, name->stream_name);
     226          49 :                 if (s->name == NULL) {
     227           0 :                         talloc_free(streams);
     228           0 :                         return NT_STATUS_NO_MEMORY;
     229             :                 }
     230             :         }
     231             : 
     232          69 :         status = pvfs_streams_save(pvfs, name, fd, streams);
     233          69 :         talloc_free(streams);
     234             : 
     235          69 :         return status;
     236             : }
     237             : 
     238             : 
     239             : /*
     240             :   rename a stream
     241             : */
     242          12 : NTSTATUS pvfs_stream_rename(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
     243             :                             const char *new_name, bool overwrite)
     244             : {
     245           0 :         struct xattr_DosStreams *streams;
     246           0 :         int i, found_old, found_new;
     247           0 :         NTSTATUS status;
     248             : 
     249          12 :         streams = talloc(name, struct xattr_DosStreams);
     250          12 :         if (streams == NULL) {
     251           0 :                 return NT_STATUS_NO_MEMORY;
     252             :         }
     253             : 
     254          12 :         new_name = stream_name_normalise(streams, new_name);
     255          12 :         if (new_name == NULL) {
     256           0 :                 return NT_STATUS_NO_MEMORY;
     257             :         }
     258             : 
     259          12 :         status = pvfs_streams_load(pvfs, name, fd, streams);
     260          12 :         if (!NT_STATUS_IS_OK(status)) {
     261           0 :                 ZERO_STRUCTP(streams);
     262             :         }
     263             : 
     264             :         /* the default stream always exists */
     265          23 :         if (strcmp(new_name, "") == 0 ||
     266          11 :             strcasecmp_m(new_name, ":$DATA") == 0) {
     267           1 :                 return NT_STATUS_OBJECT_NAME_COLLISION;         
     268             :         }
     269             : 
     270             :         /* try to find the old/new names in the list */
     271          11 :         found_old = found_new = -1;
     272          36 :         for (i=0;i<streams->num_streams;i++) {
     273          25 :                 struct xattr_DosStream *s = &streams->streams[i];
     274          25 :                 if (stream_name_cmp(s->name, new_name) == 0) {
     275           8 :                         found_new = i;
     276             :                 }
     277          25 :                 if (stream_name_cmp(s->name, name->stream_name) == 0) {
     278          11 :                         found_old = i;
     279             :                 }
     280             :         }
     281             : 
     282          11 :         if (found_old == -1) {
     283           0 :                 talloc_free(streams);
     284           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;         
     285             :         }
     286             : 
     287          11 :         if (found_new == -1) {
     288             :                 /* a simple rename */
     289           3 :                 struct xattr_DosStream *s = &streams->streams[found_old];
     290           3 :                 s->name = new_name;
     291             :         } else {
     292           8 :                 if (!overwrite) {
     293           1 :                         return NT_STATUS_OBJECT_NAME_COLLISION;
     294             :                 }
     295           7 :                 if (found_old != found_new) {
     296             :                         /* remove the old one and replace with the new one */
     297           6 :                         streams->streams[found_old].name = new_name;
     298           6 :                         memmove(&streams->streams[found_new],
     299           6 :                                 &streams->streams[found_new+1],
     300             :                                 sizeof(streams->streams[0]) *
     301           6 :                                 (streams->num_streams - (found_new+1)));
     302           6 :                         streams->num_streams--;
     303             :                 }
     304             :         }
     305             : 
     306          10 :         status = pvfs_streams_save(pvfs, name, fd, streams);
     307             : 
     308          10 :         if (NT_STATUS_IS_OK(status)) {
     309             : 
     310             :                 /* update the in-memory copy of the name of the open file */
     311          10 :                 talloc_free(name->stream_name);
     312          10 :                 name->stream_name = talloc_strdup(name, new_name);
     313             : 
     314          10 :                 talloc_free(streams);
     315             :         }
     316             : 
     317          10 :         return status;
     318             : }
     319             : 
     320             : 
     321             : /*
     322             :   create the xattr for a alternate data stream
     323             : */
     324          49 : NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs, 
     325             :                             struct pvfs_filename *name, 
     326             :                             int fd)
     327             : {
     328           0 :         NTSTATUS status;
     329          49 :         status = pvfs_xattr_create(pvfs, name->full_name, fd, 
     330          49 :                                    XATTR_DOSSTREAM_PREFIX, name->stream_name);
     331          49 :         if (!NT_STATUS_IS_OK(status)) {
     332           0 :                 return status;
     333             :         }
     334          49 :         return pvfs_stream_update_size(pvfs, name, fd, 0);
     335             : }
     336             : 
     337             : /*
     338             :   delete the xattr for a alternate data stream
     339             : */
     340           7 : NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs, 
     341             :                             struct pvfs_filename *name, 
     342             :                             int fd)
     343             : {
     344           0 :         NTSTATUS status;
     345           0 :         struct xattr_DosStreams *streams;
     346           0 :         int i;
     347             : 
     348           7 :         status = pvfs_xattr_delete(pvfs, name->full_name, fd, 
     349           7 :                                    XATTR_DOSSTREAM_PREFIX, name->stream_name);
     350           7 :         if (!NT_STATUS_IS_OK(status)) {
     351           0 :                 return status;
     352             :         }
     353             : 
     354           7 :         streams = talloc(name, struct xattr_DosStreams);
     355           7 :         if (streams == NULL) {
     356           0 :                 return NT_STATUS_NO_MEMORY;
     357             :         }
     358             : 
     359           7 :         status = pvfs_streams_load(pvfs, name, fd, streams);
     360           7 :         if (!NT_STATUS_IS_OK(status)) {
     361           0 :                 talloc_free(streams);
     362           0 :                 return status;
     363             :         }
     364             : 
     365           7 :         for (i=0;i<streams->num_streams;i++) {
     366           7 :                 struct xattr_DosStream *s = &streams->streams[i];
     367           7 :                 if (stream_name_cmp(s->name, name->stream_name) == 0) {
     368           7 :                         memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s));
     369           7 :                         streams->num_streams--;
     370           7 :                         break;
     371             :                 }
     372             :         }
     373             : 
     374           7 :         status = pvfs_streams_save(pvfs, name, fd, streams);
     375           7 :         talloc_free(streams);
     376             : 
     377           7 :         return status;
     378             : }
     379             : 
     380             : /* 
     381             :    load a stream into a blob
     382             : */
     383          38 : static NTSTATUS pvfs_stream_load(struct pvfs_state *pvfs,
     384             :                                  TALLOC_CTX *mem_ctx,
     385             :                                  struct pvfs_filename *name,
     386             :                                  int fd,
     387             :                                  size_t estimated_size,
     388             :                                  DATA_BLOB *blob)
     389             : {
     390           0 :         NTSTATUS status;
     391             : 
     392          38 :         status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd, 
     393             :                                  XATTR_DOSSTREAM_PREFIX,
     394          38 :                                  name->stream_name, estimated_size, blob);
     395             : 
     396          38 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
     397             :                 /* try with a case insensitive match */
     398           0 :                 struct xattr_DosStreams *streams;
     399           0 :                 int i;
     400             : 
     401           2 :                 streams = talloc(mem_ctx, struct xattr_DosStreams);
     402           2 :                 if (streams == NULL) {
     403           0 :                         return NT_STATUS_NO_MEMORY;
     404             :                 }
     405             :                 
     406           2 :                 status = pvfs_streams_load(pvfs, name, fd, streams);
     407           2 :                 if (!NT_STATUS_IS_OK(status)) {
     408           0 :                         talloc_free(streams);
     409           0 :                         return NT_STATUS_NOT_FOUND;
     410             :                 }
     411           4 :                 for (i=0;i<streams->num_streams;i++) {
     412           4 :                         struct xattr_DosStream *s = &streams->streams[i];
     413           4 :                         if (stream_name_cmp(s->name, name->stream_name) == 0) {
     414           2 :                                 status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd, 
     415             :                                                          XATTR_DOSSTREAM_PREFIX,
     416             :                                                          s->name, estimated_size, blob);
     417           2 :                                 talloc_free(streams);
     418           2 :                                 return status;
     419             :                         }
     420             :                 }
     421           0 :                 talloc_free(streams);
     422           0 :                 return NT_STATUS_NOT_FOUND;
     423             :         }
     424             :         
     425          36 :         return status;
     426             : }
     427             : 
     428             : /*
     429             :   the equivalent of pread() on a stream
     430             : */
     431          18 : ssize_t pvfs_stream_read(struct pvfs_state *pvfs,
     432             :                          struct pvfs_file_handle *h, void *data, size_t count, off_t offset)
     433             : {
     434           0 :         NTSTATUS status;
     435           0 :         DATA_BLOB blob;
     436          18 :         if (count == 0) {
     437           0 :                 return 0;
     438             :         }
     439          18 :         status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
     440          18 :         if (!NT_STATUS_IS_OK(status)) {
     441           0 :                 errno = EIO;
     442           0 :                 return -1;
     443             :         }
     444          18 :         if (offset >= blob.length) {
     445           6 :                 data_blob_free(&blob);
     446           6 :                 return 0;
     447             :         }
     448          12 :         if (count > blob.length - offset) {
     449          12 :                 count = blob.length - offset;
     450             :         }
     451          12 :         memcpy(data, blob.data + offset, count);
     452          12 :         data_blob_free(&blob);
     453          12 :         return count;
     454             : }
     455             : 
     456             : 
     457             : /*
     458             :   the equivalent of pwrite() on a stream
     459             : */
     460          16 : ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
     461             :                           struct pvfs_file_handle *h, const void *data, size_t count, off_t offset)
     462             : {
     463           0 :         NTSTATUS status;
     464           0 :         DATA_BLOB blob;
     465          16 :         if (count == 0) {
     466           2 :                 return 0;
     467             :         }
     468             : 
     469          14 :         if (count+offset > XATTR_MAX_STREAM_SIZE) {
     470           2 :                 if (!pvfs->ea_db || count+offset > XATTR_MAX_STREAM_SIZE_TDB) {
     471           0 :                         errno = ENOSPC;
     472           0 :                         return -1;
     473             :                 }
     474             :         }
     475             : 
     476             :         /* we have to load the existing stream, then modify, then save */
     477          14 :         status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
     478          14 :         if (!NT_STATUS_IS_OK(status)) {
     479           0 :                 blob = data_blob(NULL, 0);
     480             :         }
     481          14 :         if (count+offset > blob.length) {
     482          14 :                 blob.data = talloc_realloc(blob.data, blob.data, uint8_t, count+offset);
     483          14 :                 if (blob.data == NULL) {
     484           0 :                         errno = ENOMEM;
     485           0 :                         return -1;
     486             :                 }
     487          14 :                 if (offset > blob.length) {
     488           2 :                         memset(blob.data+blob.length, 0, offset - blob.length);
     489             :                 }
     490          14 :                 blob.length = count+offset;
     491             :         }
     492          14 :         memcpy(blob.data + offset, data, count);
     493             : 
     494          14 :         status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
     495          14 :                                  h->name->stream_name, &blob);
     496          14 :         if (!NT_STATUS_IS_OK(status)) {
     497           0 :                 data_blob_free(&blob);
     498             :                 /* getting this error mapping right is probably
     499             :                    not worth it */
     500           0 :                 errno = ENOSPC;
     501           0 :                 return -1;
     502             :         }
     503             : 
     504          14 :         status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length);
     505             : 
     506          14 :         data_blob_free(&blob);
     507             : 
     508          14 :         if (!NT_STATUS_IS_OK(status)) {
     509           0 :                 errno = EIO;
     510           0 :                 return -1;
     511             :         }
     512             : 
     513          14 :         return count;
     514             : }
     515             : 
     516             : /*
     517             :   the equivalent of truncate() on a stream
     518             : */
     519           6 : NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
     520             :                               struct pvfs_filename *name, int fd, off_t length)
     521             : {
     522           0 :         NTSTATUS status;
     523           0 :         DATA_BLOB blob;
     524             : 
     525           6 :         if (length > XATTR_MAX_STREAM_SIZE) {
     526           1 :                 if (!pvfs->ea_db || length > XATTR_MAX_STREAM_SIZE_TDB) {
     527           0 :                         return NT_STATUS_DISK_FULL;
     528             :                 }
     529             :         }
     530             : 
     531             :         /* we have to load the existing stream, then modify, then save */
     532           6 :         status = pvfs_stream_load(pvfs, name, name, fd, length, &blob);
     533           6 :         if (!NT_STATUS_IS_OK(status)) {
     534           0 :                 return status;
     535             :         }
     536           6 :         if (length <= blob.length) {
     537           3 :                 blob.length = length;
     538           3 :         } else if (length > blob.length) {
     539           3 :                 blob.data = talloc_realloc(blob.data, blob.data, uint8_t, length);
     540           3 :                 if (blob.data == NULL) {
     541           0 :                         return NT_STATUS_NO_MEMORY;
     542             :                 }
     543           3 :                 memset(blob.data+blob.length, 0, length - blob.length);
     544           3 :                 blob.length = length;
     545             :         }
     546             : 
     547           6 :         status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
     548           6 :                                  name->stream_name, &blob);
     549             : 
     550           6 :         if (NT_STATUS_IS_OK(status)) {
     551           6 :                 status = pvfs_stream_update_size(pvfs, name, fd, blob.length);
     552             :         }
     553           6 :         data_blob_free(&blob);
     554             : 
     555           6 :         return status;
     556             : }

Generated by: LCOV version 1.14