LCOV - code coverage report
Current view: top level - source3/smbd - statcache.c (source / functions) Hit Total Coverage
Test: coverage report for master 6248eab5 Lines: 114 155 73.5 %
Date: 2021-08-25 13:27:56 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    stat cache code
       4             :    Copyright (C) Andrew Tridgell 1992-2000
       5             :    Copyright (C) Jeremy Allison 1999-2007
       6             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
       7             :    Copyright (C) Volker Lendecke 2007
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "../lib/util/memcache.h"
      25             : #include "smbd/smbd.h"
      26             : #include "messages.h"
      27             : #include "serverid.h"
      28             : #include "smbprofile.h"
      29             : #include <tdb.h>
      30             : 
      31             : #define STAT_CACHE_TWRP_TOKEN "%016" PRIx64 "@%s"
      32             : #define STAT_CACHE_TWRP_TOKEN_LEN 17
      33             : 
      34             : /****************************************************************************
      35             :  Stat cache code used in unix_convert.
      36             : *****************************************************************************/
      37             : 
      38             : /**
      39             :  * Add an entry into the stat cache.
      40             :  *
      41             :  * @param full_orig_name       The original name as specified by the client
      42             :  * @param orig_translated_path The name on our filesystem.
      43             :  *
      44             :  * @note Only the first strlen(orig_translated_path) characters are stored
      45             :  *       into the cache.  This means that full_orig_name will be internally
      46             :  *       truncated.
      47             :  *
      48             :  */
      49             : 
      50      192933 : void stat_cache_add( const char *full_orig_name,
      51             :                 const char *translated_path_in,
      52             :                 NTTIME twrp,
      53             :                 bool case_sensitive)
      54             : {
      55             :         size_t translated_path_length;
      56      192933 :         char *translated_path = NULL;
      57             :         char *original_path;
      58             :         size_t original_path_length;
      59      192933 :         TALLOC_CTX *ctx = talloc_tos();
      60             : 
      61      192933 :         if (!lp_stat_cache()) {
      62           0 :                 return;
      63             :         }
      64             : 
      65             :         /*
      66             :          * Don't cache trivial valid directory entries such as . and ..
      67             :          */
      68             : 
      69      192933 :         if ((*full_orig_name == '\0')
      70      192933 :             || ISDOT(full_orig_name) || ISDOTDOT(full_orig_name)) {
      71           0 :                 return;
      72             :         }
      73             : 
      74      192933 :         translated_path = talloc_asprintf(ctx,
      75             :                                           STAT_CACHE_TWRP_TOKEN,
      76             :                                           twrp,
      77             :                                           translated_path_in);
      78      192933 :         if (translated_path == NULL) {
      79           0 :                 return;
      80             :         }
      81             : 
      82             :         /*
      83             :          * If we are in case insentive mode, we don't need to
      84             :          * store names that need no translation - else, it
      85             :          * would be a waste.
      86             :          */
      87             : 
      88      192933 :         if (!case_sensitive && (strcmp(full_orig_name, translated_path) == 0)) {
      89           0 :                 TALLOC_FREE(translated_path);
      90           0 :                 return;
      91             :         }
      92             : 
      93             :         /*
      94             :          * Remove any trailing '/' characters from the
      95             :          * translated path.
      96             :          */
      97             : 
      98      192933 :         translated_path_length = strlen(translated_path);
      99             : 
     100      192933 :         if(translated_path[translated_path_length-1] == '/') {
     101           0 :                 translated_path_length--;
     102             :         }
     103             : 
     104      192933 :         if(case_sensitive) {
     105         738 :                 original_path = talloc_asprintf(ctx,
     106             :                                                 STAT_CACHE_TWRP_TOKEN,
     107             :                                                 twrp,
     108             :                                                 full_orig_name);
     109             :         } else {
     110      192195 :                 char *upper = NULL;
     111             : 
     112      192195 :                 upper = talloc_strdup_upper(ctx, full_orig_name);
     113      192195 :                 if (upper == NULL) {
     114           0 :                         TALLOC_FREE(translated_path);
     115           0 :                         return;
     116             :                 }
     117      192195 :                 original_path = talloc_asprintf(ctx,
     118             :                                                 STAT_CACHE_TWRP_TOKEN,
     119             :                                                 twrp,
     120             :                                                 upper);
     121      192195 :                 TALLOC_FREE(upper);
     122             :         }
     123             : 
     124      192933 :         if (!original_path) {
     125           0 :                 TALLOC_FREE(translated_path);
     126           0 :                 return;
     127             :         }
     128             : 
     129      192933 :         original_path_length = strlen(original_path);
     130             : 
     131      192933 :         if(original_path[original_path_length-1] == '/') {
     132           0 :                 original_path[original_path_length-1] = '\0';
     133           0 :                 original_path_length--;
     134             :         }
     135             : 
     136      192933 :         if (original_path_length != translated_path_length) {
     137        7428 :                 if (original_path_length < translated_path_length) {
     138           0 :                         DEBUG(0, ("OOPS - tried to store stat cache entry "
     139             :                         "for weird length paths [%s] %lu and [%s] %lu)!\n",
     140             :                                   original_path,
     141             :                                   (unsigned long)original_path_length,
     142             :                                   translated_path,
     143             :                                   (unsigned long)translated_path_length));
     144           0 :                         TALLOC_FREE(original_path);
     145           0 :                         TALLOC_FREE(translated_path);
     146           0 :                         return;
     147             :                 }
     148             : 
     149             :                 /* we only want to index by the first part of original_path,
     150             :                         up to the length of translated_path */
     151             : 
     152        7428 :                 original_path[translated_path_length] = '\0';
     153        7428 :                 original_path_length = translated_path_length;
     154             :         }
     155             : 
     156             :         /* Ensure we're null terminated. */
     157      192933 :         translated_path[translated_path_length] = '\0';
     158             : 
     159             :         /*
     160             :          * New entry or replace old entry.
     161             :          */
     162             : 
     163      192933 :         memcache_add(
     164             :                 smbd_memcache(), STAT_CACHE,
     165             :                 data_blob_const(original_path, original_path_length),
     166             :                 data_blob_const(translated_path, translated_path_length + 1));
     167             : 
     168      192933 :         DEBUG(5,("stat_cache_add: Added entry (%lx:size %x) %s -> %s\n",
     169             :                  (unsigned long)translated_path,
     170             :                  (unsigned int)translated_path_length,
     171             :                  original_path,
     172             :                  translated_path));
     173             : 
     174      192933 :         TALLOC_FREE(original_path);
     175      192933 :         TALLOC_FREE(translated_path);
     176             : }
     177             : 
     178             : /**
     179             :  * Look through the stat cache for an entry
     180             :  *
     181             :  * @param conn    A connection struct to do the stat() with.
     182             :  * @param posix_paths Whether to lookup using stat() or lstat()
     183             :  * @param name    The path we are attempting to cache, modified by this routine
     184             :  *                to be correct as far as the cache can tell us. We assume that
     185             :  *                it is a talloc'ed string from top of stack, we free it if
     186             :  *                necessary.
     187             :  * @param dirpath The path as far as the stat cache told us. Also talloced
     188             :  *                from top of stack.
     189             :  * @param start   A pointer into name, for where to 'start' in fixing the rest
     190             :  *                of the name up.
     191             :  * @param psd     A stat buffer, NOT from the cache, but just a side-effect.
     192             :  *
     193             :  * @return True if we translated (and did a scuccessful stat on) the entire
     194             :  *                name.
     195             :  *
     196             :  */
     197             : 
     198      591117 : bool stat_cache_lookup(connection_struct *conn,
     199             :                         bool posix_paths,
     200             :                         char **pp_name,
     201             :                         char **pp_dirpath,
     202             :                         char **pp_start,
     203             :                         NTTIME twrp,
     204             :                         SMB_STRUCT_STAT *pst)
     205             : {
     206             :         char *chk_name;
     207             :         size_t namelen;
     208      591117 :         bool sizechanged = False;
     209      591117 :         unsigned int num_components = 0;
     210             :         char *translated_path;
     211             :         size_t translated_path_length;
     212             :         DATA_BLOB data_val;
     213             :         char *name;
     214      591117 :         TALLOC_CTX *ctx = talloc_tos();
     215             :         struct smb_filename smb_fname;
     216             :         int ret;
     217             : 
     218      591117 :         *pp_dirpath = NULL;
     219      591117 :         *pp_start = *pp_name;
     220             : 
     221      591117 :         if (!lp_stat_cache()) {
     222           0 :                 return False;
     223             :         }
     224             : 
     225      591117 :         name = *pp_name;
     226      591117 :         namelen = strlen(name);
     227             : 
     228      591117 :         DO_PROFILE_INC(statcache_lookups);
     229             : 
     230             :         /*
     231             :          * Don't lookup trivial valid directory entries.
     232             :          */
     233      591117 :         if ((*name == '\0') || ISDOT(name) || ISDOTDOT(name)) {
     234           0 :                 return False;
     235             :         }
     236             : 
     237      591117 :         if (conn->case_sensitive) {
     238           0 :                 chk_name = talloc_asprintf(ctx,
     239             :                                            STAT_CACHE_TWRP_TOKEN,
     240             :                                            twrp,
     241             :                                            name);
     242           0 :                 if (!chk_name) {
     243           0 :                         DEBUG(0, ("stat_cache_lookup: strdup failed!\n"));
     244           0 :                         return False;
     245             :                 }
     246             : 
     247             :         } else {
     248      591117 :                 char *upper = NULL;
     249             : 
     250      591117 :                 upper = talloc_strdup_upper(ctx,name);
     251      591117 :                 if (upper == NULL) {
     252           0 :                         DBG_ERR("talloc_strdup_upper failed!\n");
     253           0 :                         return False;
     254             :                 }
     255      591117 :                 chk_name = talloc_asprintf(ctx,
     256             :                                            STAT_CACHE_TWRP_TOKEN,
     257             :                                            twrp,
     258             :                                            upper);
     259      591117 :                 if (!chk_name) {
     260           0 :                         DEBUG(0, ("stat_cache_lookup: talloc_strdup_upper failed!\n"));
     261           0 :                         return False;
     262             :                 }
     263             : 
     264             :                 /*
     265             :                  * In some language encodings the length changes
     266             :                  * if we uppercase. We need to treat this differently
     267             :                  * below.
     268             :                  */
     269      591117 :                 if (strlen(chk_name) != namelen) {
     270      591117 :                         sizechanged = True;
     271             :                 }
     272             :         }
     273             : 
     274      453686 :         while (1) {
     275             :                 char *sp;
     276             : 
     277     1045162 :                 data_val = data_blob_null;
     278             : 
     279     1045162 :                 if (memcache_lookup(
     280             :                             smbd_memcache(), STAT_CACHE,
     281             :                             data_blob_const(chk_name, strlen(chk_name)),
     282             :                             &data_val)) {
     283      366585 :                         break;
     284             :                 }
     285             : 
     286      669242 :                 DEBUG(10,("stat_cache_lookup: lookup failed for name [%s]\n",
     287             :                                 chk_name ));
     288             :                 /*
     289             :                  * Didn't find it - remove last component for next try.
     290             :                  */
     291      669242 :                 if (!(sp = strrchr_m(chk_name, '/'))) {
     292             :                         /*
     293             :                          * We reached the end of the name - no match.
     294             :                          */
     295      215197 :                         DO_PROFILE_INC(statcache_misses);
     296      215197 :                         TALLOC_FREE(chk_name);
     297      215197 :                         return False;
     298             :                 }
     299             : 
     300      454045 :                 *sp = '\0';
     301             : 
     302             :                 /*
     303             :                  * Count the number of times we have done this, we'll
     304             :                  * need it when reconstructing the string.
     305             :                  */
     306      454045 :                 num_components++;
     307             : 
     308      454045 :                 if ((*chk_name == '\0')
     309      454045 :                     || ISDOT(chk_name) || ISDOTDOT(chk_name)) {
     310           0 :                         DO_PROFILE_INC(statcache_misses);
     311           0 :                         TALLOC_FREE(chk_name);
     312           0 :                         return False;
     313             :                 }
     314             :         }
     315             : 
     316      375920 :         SMB_ASSERT(data_val.length >= STAT_CACHE_TWRP_TOKEN_LEN);
     317             : 
     318      375920 :         translated_path = talloc_strdup(
     319      375920 :                 ctx,(char *)data_val.data + STAT_CACHE_TWRP_TOKEN_LEN);
     320      375920 :         if (!translated_path) {
     321           0 :                 smb_panic("talloc failed");
     322             :         }
     323      375920 :         translated_path_length = data_val.length - 1 - STAT_CACHE_TWRP_TOKEN_LEN;
     324             : 
     325      375920 :         DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] "
     326             :                   "-> [%s]\n", chk_name, translated_path ));
     327      375920 :         DO_PROFILE_INC(statcache_hits);
     328             : 
     329      375920 :         smb_fname = (struct smb_filename) {
     330             :                 .base_name = translated_path,
     331             :                 .twrp = twrp,
     332             :         };
     333             : 
     334      375920 :         if (posix_paths) {
     335         704 :                 ret = SMB_VFS_LSTAT(conn, &smb_fname);
     336             :         } else {
     337      375216 :                 ret = SMB_VFS_STAT(conn, &smb_fname);
     338             :         }
     339             : 
     340      375920 :         if (ret != 0) {
     341             :                 /* Discard this entry - it doesn't exist in the filesystem. */
     342       93529 :                 memcache_delete(smbd_memcache(), STAT_CACHE,
     343             :                                 data_blob_const(chk_name, strlen(chk_name)));
     344       93529 :                 TALLOC_FREE(chk_name);
     345       93529 :                 TALLOC_FREE(translated_path);
     346       93529 :                 return False;
     347             :         }
     348             :         /*
     349             :          * Only copy the stat struct back if we actually hit the full path
     350             :          */
     351      282391 :         if (num_components == 0) {
     352      109839 :                 *pst = smb_fname.st;
     353             :         }
     354             : 
     355      282391 :         if (!sizechanged) {
     356           0 :                 memcpy(*pp_name, translated_path,
     357             :                        MIN(namelen, translated_path_length));
     358             :         } else {
     359      282391 :                 if (num_components == 0) {
     360      109839 :                         name = talloc_strndup(ctx, translated_path,
     361             :                                            translated_path_length);
     362             :                 } else {
     363             :                         char *sp;
     364             : 
     365      172552 :                         sp = strnrchr_m(name, '/', num_components);
     366      172552 :                         if (sp) {
     367      172552 :                                 name = talloc_asprintf(ctx,"%.*s%s",
     368             :                                          (int)translated_path_length,
     369             :                                          translated_path, sp);
     370             :                         } else {
     371           0 :                                 name = talloc_strndup(ctx,
     372             :                                                 translated_path,
     373             :                                                 translated_path_length);
     374             :                         }
     375             :                 }
     376      282391 :                 if (name == NULL) {
     377             :                         /*
     378             :                          * TODO: Get us out of here with a real error message
     379             :                          */
     380           0 :                         smb_panic("talloc failed");
     381             :                 }
     382      282391 :                 TALLOC_FREE(*pp_name);
     383      282391 :                 *pp_name = name;
     384             :         }
     385             : 
     386             : 
     387             :         /* set pointer for 'where to start' on fixing the rest of the name */
     388      282391 :         *pp_start = &name[translated_path_length];
     389      282391 :         if (**pp_start == '/') {
     390      172552 :                 ++*pp_start;
     391             :         }
     392             : 
     393      282391 :         *pp_dirpath = translated_path;
     394      282391 :         TALLOC_FREE(chk_name);
     395      282391 :         return (namelen == translated_path_length);
     396             : }
     397             : 
     398             : /***************************************************************************
     399             :  Tell all smbd's to delete an entry.
     400             : **************************************************************************/
     401             : 
     402       10199 : void smbd_send_stat_cache_delete_message(struct messaging_context *msg_ctx,
     403             :                                          const char *name)
     404             : {
     405             : #ifdef DEVELOPER
     406       10199 :         messaging_send_all(msg_ctx,
     407             :                            MSG_SMB_STAT_CACHE_DELETE,
     408             :                            name,
     409       10199 :                            strlen(name)+1);
     410             : #endif
     411       10199 : }
     412             : 
     413             : /***************************************************************************
     414             :  Delete an entry.
     415             : **************************************************************************/
     416             : 
     417       15551 : void stat_cache_delete(const char *name)
     418             : {
     419       15551 :         char *upper = talloc_strdup_upper(talloc_tos(), name);
     420       15551 :         char *lname = NULL;
     421             : 
     422       15551 :         if (upper == NULL) {
     423           0 :                 return;
     424             :         }
     425             : 
     426       15551 :         lname = talloc_asprintf(talloc_tos(),
     427             :                                 STAT_CACHE_TWRP_TOKEN,
     428             :                                 (uintmax_t)0,
     429             :                                 upper);
     430       15551 :         TALLOC_FREE(upper);
     431       15551 :         if (lname == NULL) {
     432           0 :                 return;
     433             :         }
     434       15551 :         DEBUG(10,("stat_cache_delete: deleting name [%s] -> %s\n",
     435             :                         lname, name ));
     436             : 
     437       15551 :         memcache_delete(smbd_memcache(), STAT_CACHE,
     438       15551 :                         data_blob_const(lname, talloc_get_size(lname)-1));
     439       15551 :         TALLOC_FREE(lname);
     440             : }
     441             : 
     442             : /***************************************************************
     443             :  Compute a hash value based on a string key value.
     444             :  The function returns the bucket index number for the hashed key.
     445             :  JRA. Use a djb-algorithm hash for speed.
     446             : ***************************************************************/
     447             : 
     448           0 : unsigned int fast_string_hash(TDB_DATA *key)
     449             : {
     450           0 :         unsigned int n = 0;
     451             :         const char *p;
     452           0 :         for (p = (const char *)key->dptr; *p != '\0'; p++) {
     453           0 :                 n = ((n << 5) + n) ^ (unsigned int)(*p);
     454             :         }
     455           0 :         return n;
     456             : }
     457             : 
     458             : /***************************************************************************
     459             :  Initializes or clears the stat cache.
     460             : **************************************************************************/
     461             : 
     462        1188 : bool reset_stat_cache( void )
     463             : {
     464        1188 :         if (!lp_stat_cache())
     465           0 :                 return True;
     466             : 
     467        1188 :         memcache_flush(smbd_memcache(), STAT_CACHE);
     468             : 
     469        1188 :         return True;
     470             : }

Generated by: LCOV version 1.13