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

Generated by: LCOV version 1.14