LCOV - code coverage report
Current view: top level - source3/smbd - statcache.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 19 148 12.8 %
Date: 2024-06-13 04:01:37 Functions: 3 5 60.0 %

          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           0 : 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           0 :         char *translated_path = NULL;
      57             :         char *original_path;
      58             :         size_t original_path_length;
      59           0 :         TALLOC_CTX *ctx = talloc_tos();
      60             : 
      61           0 :         if (!lp_stat_cache()) {
      62           0 :                 return;
      63             :         }
      64             : 
      65             :         /*
      66             :          * Don't cache trivial valid directory entries such as . and ..
      67             :          */
      68             : 
      69           0 :         if ((*full_orig_name == '\0')
      70           0 :             || ISDOT(full_orig_name) || ISDOTDOT(full_orig_name)) {
      71           0 :                 return;
      72             :         }
      73             : 
      74           0 :         translated_path = talloc_asprintf(ctx,
      75             :                                           STAT_CACHE_TWRP_TOKEN,
      76             :                                           twrp,
      77             :                                           translated_path_in);
      78           0 :         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           0 :         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           0 :         translated_path_length = strlen(translated_path);
      99             : 
     100           0 :         if(translated_path[translated_path_length-1] == '/') {
     101           0 :                 translated_path_length--;
     102             :         }
     103             : 
     104           0 :         if(case_sensitive) {
     105           0 :                 original_path = talloc_asprintf(ctx,
     106             :                                                 STAT_CACHE_TWRP_TOKEN,
     107             :                                                 twrp,
     108             :                                                 full_orig_name);
     109             :         } else {
     110           0 :                 char *upper = NULL;
     111             : 
     112           0 :                 upper = talloc_strdup_upper(ctx, full_orig_name);
     113           0 :                 if (upper == NULL) {
     114           0 :                         TALLOC_FREE(translated_path);
     115           0 :                         return;
     116             :                 }
     117           0 :                 original_path = talloc_asprintf(ctx,
     118             :                                                 STAT_CACHE_TWRP_TOKEN,
     119             :                                                 twrp,
     120             :                                                 upper);
     121           0 :                 TALLOC_FREE(upper);
     122             :         }
     123             : 
     124           0 :         if (!original_path) {
     125           0 :                 TALLOC_FREE(translated_path);
     126           0 :                 return;
     127             :         }
     128             : 
     129           0 :         original_path_length = strlen(original_path);
     130             : 
     131           0 :         if(original_path[original_path_length-1] == '/') {
     132           0 :                 original_path[original_path_length-1] = '\0';
     133           0 :                 original_path_length--;
     134             :         }
     135             : 
     136           0 :         if (original_path_length != translated_path_length) {
     137           0 :                 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           0 :                 original_path[translated_path_length] = '\0';
     153           0 :                 original_path_length = translated_path_length;
     154             :         }
     155             : 
     156             :         /* Ensure we're null terminated. */
     157           0 :         translated_path[translated_path_length] = '\0';
     158             : 
     159             :         /*
     160             :          * New entry or replace old entry.
     161             :          */
     162             : 
     163           0 :         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           0 :         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           0 :         TALLOC_FREE(original_path);
     175           0 :         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 successful stat on) the entire
     194             :  *                name.
     195             :  *
     196             :  */
     197             : 
     198           0 : bool stat_cache_lookup(connection_struct *conn,
     199             :                         char **pp_name,
     200             :                         char **pp_dirpath,
     201             :                         char **pp_start,
     202             :                         NTTIME twrp,
     203             :                         SMB_STRUCT_STAT *pst)
     204             : {
     205             :         char *chk_name;
     206             :         size_t namelen;
     207           0 :         bool sizechanged = False;
     208           0 :         unsigned int num_components = 0;
     209             :         char *translated_path;
     210             :         size_t translated_path_length;
     211             :         DATA_BLOB data_val;
     212             :         char *name;
     213           0 :         TALLOC_CTX *ctx = talloc_tos();
     214             :         struct smb_filename smb_fname;
     215             :         int ret;
     216             : 
     217           0 :         *pp_dirpath = NULL;
     218           0 :         *pp_start = *pp_name;
     219             : 
     220           0 :         if (!lp_stat_cache()) {
     221           0 :                 return False;
     222             :         }
     223             : 
     224           0 :         name = *pp_name;
     225           0 :         namelen = strlen(name);
     226             : 
     227           0 :         DO_PROFILE_INC(statcache_lookups);
     228             : 
     229             :         /*
     230             :          * Don't lookup trivial valid directory entries.
     231             :          */
     232           0 :         if ((*name == '\0') || ISDOT(name) || ISDOTDOT(name)) {
     233           0 :                 return False;
     234             :         }
     235             : 
     236           0 :         if (conn->case_sensitive) {
     237           0 :                 chk_name = talloc_asprintf(ctx,
     238             :                                            STAT_CACHE_TWRP_TOKEN,
     239             :                                            twrp,
     240             :                                            name);
     241           0 :                 if (!chk_name) {
     242           0 :                         DEBUG(0, ("stat_cache_lookup: strdup failed!\n"));
     243           0 :                         return False;
     244             :                 }
     245             : 
     246             :         } else {
     247           0 :                 char *upper = NULL;
     248             : 
     249           0 :                 upper = talloc_strdup_upper(ctx,name);
     250           0 :                 if (upper == NULL) {
     251           0 :                         DBG_ERR("talloc_strdup_upper failed!\n");
     252           0 :                         return False;
     253             :                 }
     254           0 :                 chk_name = talloc_asprintf(ctx,
     255             :                                            STAT_CACHE_TWRP_TOKEN,
     256             :                                            twrp,
     257             :                                            upper);
     258           0 :                 if (!chk_name) {
     259           0 :                         DEBUG(0, ("stat_cache_lookup: talloc_strdup_upper failed!\n"));
     260           0 :                         return False;
     261             :                 }
     262             : 
     263             :                 /*
     264             :                  * In some language encodings the length changes
     265             :                  * if we uppercase. We need to treat this differently
     266             :                  * below.
     267             :                  */
     268           0 :                 if (strlen(chk_name) != namelen) {
     269           0 :                         sizechanged = True;
     270             :                 }
     271             :         }
     272             : 
     273           0 :         while (1) {
     274             :                 char *sp;
     275             : 
     276           0 :                 data_val = data_blob_null;
     277             : 
     278           0 :                 if (memcache_lookup(
     279             :                             smbd_memcache(), STAT_CACHE,
     280             :                             data_blob_const(chk_name, strlen(chk_name)),
     281             :                             &data_val)) {
     282           0 :                         break;
     283             :                 }
     284             : 
     285           0 :                 DEBUG(10,("stat_cache_lookup: lookup failed for name [%s]\n",
     286             :                                 chk_name ));
     287             :                 /*
     288             :                  * Didn't find it - remove last component for next try.
     289             :                  */
     290           0 :                 if (!(sp = strrchr_m(chk_name, '/'))) {
     291             :                         /*
     292             :                          * We reached the end of the name - no match.
     293             :                          */
     294           0 :                         DO_PROFILE_INC(statcache_misses);
     295           0 :                         TALLOC_FREE(chk_name);
     296           0 :                         return False;
     297             :                 }
     298             : 
     299           0 :                 *sp = '\0';
     300             : 
     301             :                 /*
     302             :                  * Count the number of times we have done this, we'll
     303             :                  * need it when reconstructing the string.
     304             :                  */
     305           0 :                 num_components++;
     306             : 
     307           0 :                 if ((*chk_name == '\0')
     308           0 :                     || ISDOT(chk_name) || ISDOTDOT(chk_name)) {
     309           0 :                         DO_PROFILE_INC(statcache_misses);
     310           0 :                         TALLOC_FREE(chk_name);
     311           0 :                         return False;
     312             :                 }
     313             :         }
     314             : 
     315           0 :         SMB_ASSERT(data_val.length >= STAT_CACHE_TWRP_TOKEN_LEN);
     316             : 
     317           0 :         translated_path = talloc_strdup(
     318           0 :                 ctx,(char *)data_val.data + STAT_CACHE_TWRP_TOKEN_LEN);
     319           0 :         if (!translated_path) {
     320           0 :                 smb_panic("talloc failed");
     321             :         }
     322           0 :         translated_path_length = data_val.length - 1 - STAT_CACHE_TWRP_TOKEN_LEN;
     323             : 
     324           0 :         DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] "
     325             :                   "-> [%s]\n", chk_name, translated_path ));
     326           0 :         DO_PROFILE_INC(statcache_hits);
     327             : 
     328           0 :         smb_fname = (struct smb_filename) {
     329             :                 .base_name = translated_path,
     330             :                 .twrp = twrp,
     331             :         };
     332             : 
     333           0 :         ret = vfs_stat(conn, &smb_fname);
     334           0 :         if (ret != 0) {
     335             :                 /* Discard this entry - it doesn't exist in the filesystem. */
     336           0 :                 memcache_delete(smbd_memcache(), STAT_CACHE,
     337             :                                 data_blob_const(chk_name, strlen(chk_name)));
     338           0 :                 TALLOC_FREE(chk_name);
     339           0 :                 TALLOC_FREE(translated_path);
     340           0 :                 return False;
     341             :         }
     342             :         /*
     343             :          * Only copy the stat struct back if we actually hit the full path
     344             :          */
     345           0 :         if (num_components == 0) {
     346           0 :                 *pst = smb_fname.st;
     347             :         }
     348             : 
     349           0 :         if (!sizechanged) {
     350           0 :                 memcpy(*pp_name, translated_path,
     351             :                        MIN(namelen, translated_path_length));
     352             :         } else {
     353           0 :                 if (num_components == 0) {
     354           0 :                         name = talloc_strndup(ctx, translated_path,
     355             :                                            translated_path_length);
     356             :                 } else {
     357             :                         char *sp;
     358             : 
     359           0 :                         sp = strnrchr_m(name, '/', num_components);
     360           0 :                         if (sp) {
     361           0 :                                 name = talloc_asprintf(ctx,"%.*s%s",
     362             :                                          (int)translated_path_length,
     363             :                                          translated_path, sp);
     364             :                         } else {
     365           0 :                                 name = talloc_strndup(ctx,
     366             :                                                 translated_path,
     367             :                                                 translated_path_length);
     368             :                         }
     369             :                 }
     370           0 :                 if (name == NULL) {
     371             :                         /*
     372             :                          * TODO: Get us out of here with a real error message
     373             :                          */
     374           0 :                         smb_panic("talloc failed");
     375             :                 }
     376           0 :                 TALLOC_FREE(*pp_name);
     377           0 :                 *pp_name = name;
     378             :         }
     379             : 
     380             : 
     381             :         /* set pointer for 'where to start' on fixing the rest of the name */
     382           0 :         *pp_start = &name[translated_path_length];
     383           0 :         if (**pp_start == '/') {
     384           0 :                 ++*pp_start;
     385             :         }
     386             : 
     387           0 :         *pp_dirpath = translated_path;
     388           0 :         TALLOC_FREE(chk_name);
     389           0 :         return (namelen == translated_path_length);
     390             : }
     391             : 
     392             : /***************************************************************************
     393             :  Tell all smbd's to delete an entry.
     394             : **************************************************************************/
     395             : 
     396         808 : void smbd_send_stat_cache_delete_message(struct messaging_context *msg_ctx,
     397             :                                          const char *name)
     398             : {
     399             : #ifdef DEVELOPER
     400         808 :         messaging_send_all(msg_ctx,
     401             :                            MSG_SMB_STAT_CACHE_DELETE,
     402             :                            name,
     403         808 :                            strlen(name)+1);
     404             : #endif
     405         808 : }
     406             : 
     407             : /***************************************************************************
     408             :  Delete an entry.
     409             : **************************************************************************/
     410             : 
     411        2165 : void stat_cache_delete(const char *name)
     412             : {
     413        2165 :         char *upper = talloc_strdup_upper(talloc_tos(), name);
     414        2165 :         char *lname = NULL;
     415             : 
     416        2165 :         if (upper == NULL) {
     417           0 :                 return;
     418             :         }
     419             : 
     420        2165 :         lname = talloc_asprintf(talloc_tos(),
     421             :                                 STAT_CACHE_TWRP_TOKEN,
     422             :                                 (uint64_t)0,
     423             :                                 upper);
     424        2165 :         TALLOC_FREE(upper);
     425        2165 :         if (lname == NULL) {
     426           0 :                 return;
     427             :         }
     428        2165 :         DEBUG(10,("stat_cache_delete: deleting name [%s] -> %s\n",
     429             :                         lname, name ));
     430             : 
     431        2165 :         memcache_delete(smbd_memcache(), STAT_CACHE,
     432        2165 :                         data_blob_const(lname, talloc_get_size(lname)-1));
     433        2165 :         TALLOC_FREE(lname);
     434             : }
     435             : 
     436             : /***************************************************************************
     437             :  Initializes or clears the stat cache.
     438             : **************************************************************************/
     439             : 
     440         233 : bool reset_stat_cache( void )
     441             : {
     442         233 :         if (!lp_stat_cache())
     443           0 :                 return True;
     444             : 
     445         233 :         memcache_flush(smbd_memcache(), STAT_CACHE);
     446             : 
     447         233 :         return True;
     448             : }

Generated by: LCOV version 1.13