1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright (c) 1996-2001 by Sun Microsystems, Inc.
  24  * All rights reserved.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  *
  31  *                      stats_dbm.c
  32  *
  33  * Routines for dbm access.
  34  */
  35 
  36 #include <stdio.h>
  37 #include <stdlib.h>
  38 #include <stddef.h>
  39 #include <sys/types.h>
  40 #include <sys/param.h>
  41 #include <fcntl.h>
  42 #include <libintl.h>
  43 #include <time.h>
  44 #include <string.h>
  45 #include <sys/fs/cachefs_fs.h>
  46 #include "stats.h"
  47 #include <assert.h>
  48 #include <ndbm.h>
  49 
  50 void
  51 stats_dbm_open(stats_cookie_t *st)
  52 {
  53         char *tmpdir;
  54         pid_t   getpid();
  55 
  56         assert(stats_good(st));
  57         assert(! (st->st_flags & ST_DBMOPEN));
  58 
  59         if ((tmpdir = getenv("TMPDIR")) == NULL)
  60                 tmpdir = "/tmp";
  61 
  62         (void) snprintf(st->st_dbm_name, sizeof (st->st_dbm_name), "%s/%s-%d",
  63             tmpdir, st->st_progname, getpid());
  64         st->st_dbm = dbm_open(st->st_dbm_name, O_RDWR | O_CREAT, 0666);
  65         if (st->st_dbm == NULL) {
  66                 stats_perror(st, SE_FILE,
  67                     gettext("Cannot open dbm file %s"), st->st_dbm_name);
  68                 return;
  69         }
  70 
  71         st->st_flags |= ST_DBMOPEN;
  72 }
  73 
  74 void
  75 stats_dbm_rm(stats_cookie_t *st)
  76 {
  77         char buffy[MAXPATHLEN], *eobase;
  78         int unlink(), buflen, eobaselen;
  79 
  80         assert(stats_good(st));
  81 
  82         if (! (st->st_flags & ST_DBMOPEN))
  83                 return;
  84 
  85         if (strlcpy(buffy, st->st_dbm_name, sizeof (buffy)) >
  86             ((sizeof (buffy)) - (sizeof (".xxx"))))
  87                 return; /* No space for the file extensions */
  88         buflen = strlen(buffy);
  89         eobase = buffy + buflen;
  90         eobaselen = (sizeof (buffy)) - buflen;
  91 
  92         (void) strlcpy(eobase, ".dir", eobaselen);
  93         (void) unlink(buffy);
  94 
  95         (void) strlcpy(eobase, ".pag", eobaselen);
  96         (void) unlink(buffy);
  97 }
  98 
  99 void
 100 stats_dbm_close(stats_cookie_t *st)
 101 {
 102         assert(stats_good(st));
 103 
 104         if (! (st->st_flags & ST_DBMOPEN))
 105                 return;
 106 
 107         st->st_flags &= ~ST_DBMOPEN;
 108 
 109         if (st->st_dbm == NULL)
 110                 return;
 111 
 112         dbm_close(st->st_dbm);
 113 }
 114 
 115 fid_info *
 116 stats_dbm_fetch_byfid(stats_cookie_t *st, cfs_fid_t *fidp)
 117 {
 118         datum key, value;
 119         fid_info *rc;
 120 
 121         assert(stats_good(st));
 122         assert(st->st_flags & ST_DBMOPEN);
 123 
 124         key.dptr = (char *)fidp;
 125         key.dsize = sizeof (*fidp);
 126         value = dbm_fetch(st->st_dbm, key);
 127 
 128         assert((value.dptr == NULL) || (value.dsize == sizeof (fid_info)));
 129         if (value.dptr == NULL)
 130                 return (NULL);
 131 
 132         if ((rc = malloc(sizeof (*rc))) == NULL) {
 133                 stats_perror(st, SE_NOMEM,
 134                     gettext("Cannot malloc memory for fid_info record"));
 135                 return (NULL);
 136         }
 137 
 138         memcpy(rc, value.dptr, sizeof (*rc));
 139         if (rc->fi_magic != FI_MAGIC) {
 140                 free(rc);
 141                 return (NULL);
 142         }
 143 
 144         return (rc);
 145 }
 146 
 147 void
 148 stats_dbm_store_byfid(stats_cookie_t *st, cfs_fid_t *fidp, fid_info *fi)
 149 {
 150         datum key, value;
 151 
 152         assert(stats_good(st));
 153         assert(st->st_flags & ST_DBMOPEN);
 154 
 155         fi->fi_magic = FI_MAGIC;
 156 
 157         key.dptr = (char *)fidp;
 158         key.dsize = sizeof (*fidp);
 159 
 160         value.dptr = (char *)fi;
 161         value.dsize = sizeof (*fi);
 162 
 163         if (dbm_store(st->st_dbm, key, value, DBM_REPLACE) != 0) {
 164                 stats_perror(st, SE_FILE,
 165                     gettext("Cannot store fid info"));
 166                 return;
 167         }
 168 }
 169 
 170 mount_info *
 171 stats_dbm_fetch_byvfsp(stats_cookie_t *st, caddr_t vfsp)
 172 {
 173         mount_info *rc, *mi;
 174         int len1, len2, size;
 175 
 176         datum key, value;
 177 
 178         assert(stats_good(st));
 179         assert(st->st_flags & ST_DBMOPEN);
 180 
 181         key.dptr = (char *)&vfsp;
 182         key.dsize = sizeof (vfsp);
 183         value = dbm_fetch(st->st_dbm, key);
 184 
 185         if (value.dptr == NULL)
 186                 return (NULL);
 187 
 188         mi = (mount_info *)value.dptr;
 189 
 190         len1 = strlen(mi->mi_path);
 191         len2 = strlen(mi->mi_path + len1 + 1);
 192         size = sizeof (*rc) + len1 + len2 - CLPAD(mount_info, mi_path);
 193 
 194         if ((rc = malloc(size)) == NULL) {
 195                 stats_perror(st, SE_NOMEM,
 196                     gettext("Cannot malloc memory for mountinfo"));
 197                 return (NULL);
 198         }
 199         memcpy(rc, mi, size);
 200 
 201         if (rc->mi_magic != MI_MAGIC) {
 202                 free(rc);
 203                 return (NULL);
 204         }
 205 
 206         return (rc);
 207 }
 208 
 209 void
 210 stats_dbm_store_byvfsp(stats_cookie_t *st, caddr_t vfsp, mount_info *mi)
 211 {
 212         datum key, value;
 213         int len1, len2;
 214 
 215         assert(stats_good(st));
 216         assert(st->st_flags & ST_DBMOPEN);
 217 
 218         mi->mi_magic = MI_MAGIC;
 219 
 220         key.dptr = (char *)&vfsp;
 221         key.dsize = sizeof (vfsp);
 222 
 223         len1 = strlen(mi->mi_path);
 224         len2 = strlen(mi->mi_path + len1 + 1);
 225         value.dptr = (char *)mi;
 226         value.dsize = sizeof (*mi) +
 227             len1 + len2 -
 228             CLPAD(mount_info, mi_path);
 229 
 230         if (dbm_store(st->st_dbm, key, value, DBM_REPLACE) != 0) {
 231                 stats_perror(st, SE_FILE,
 232                     gettext("Cannot store mount info"));
 233                 return;
 234         }
 235 }
 236 
 237 void
 238 stats_dbm_delete_byvfsp(stats_cookie_t *st, caddr_t vfsp)
 239 {
 240         datum key;
 241 
 242         assert(stats_good(st));
 243         assert(st->st_flags & ST_DBMOPEN);
 244 
 245         key.dptr = (caddr_t)&vfsp;
 246         key.dsize = sizeof (vfsp);
 247 
 248         (void) dbm_delete(st->st_dbm, key);
 249 }
 250 
 251 datum
 252 stats_dbm_firstkey(stats_cookie_t *st)
 253 {
 254         assert(stats_good(st));
 255         assert(st->st_flags & ST_DBMOPEN);
 256 
 257         return (dbm_firstkey(st->st_dbm));
 258 }
 259 
 260 datum
 261 stats_dbm_nextkey(stats_cookie_t *st)
 262 {
 263         assert(stats_good(st));
 264         assert(st->st_flags & ST_DBMOPEN);
 265 
 266         return (dbm_nextkey(st->st_dbm));
 267 }
 268 
 269 /*
 270  * count var will be non-zero only for the record type CACHEFS_LOG_MDCREATE
 271  * and count can't be >2GB because it refers to the number of entries in
 272  * the attribute cache file.
 273  */
 274 size_t
 275 stats_dbm_attrcache_addsize(stats_cookie_t *st, mount_info *mi,
 276     ino64_t fileno, uint_t count)
 277 {
 278         char keystring[BUFSIZ];
 279         datum key, value;
 280         char *cacheid;
 281         fg_info fg, *fgp = NULL;
 282         size_t size = 0, overhead = 0;
 283         uchar_t tbits;
 284         int i;
 285         uint_t gfileno;
 286 
 287         assert(stats_good(st));
 288         assert(st->st_flags & ST_DBMOPEN);
 289 
 290         /* look up any known data about this filegrp */
 291         cacheid = mi->mi_path + strlen(mi->mi_path) + 1;
 292         (void) snprintf(keystring, sizeof (keystring), "%s.%lld", cacheid,
 293             fileno / (ino64_t)mi->mi_filegrp_size);
 294         gfileno = (uint_t)(fileno % (ino64_t)mi->mi_filegrp_size);
 295         key.dsize = strlen(keystring); /* no need to null terminate */
 296         key.dptr = keystring;
 297         value = dbm_fetch(st->st_dbm, key);
 298 
 299         size = sizeof (struct attrcache_header);
 300         size += mi->mi_filegrp_size * sizeof (struct attrcache_index);
 301         size += mi->mi_filegrp_size / NBBY;
 302 
 303         if ((value.dptr != NULL) && (value.dsize == sizeof (fg))) {
 304                 /* align the structure */
 305                 memcpy((char *)&fg, value.dptr, sizeof (fg));
 306                 fgp = &fg;
 307                 if (fgp->fg_magic != FG_MAGIC)
 308                         fgp = NULL; /* oops -- key collision! */
 309         }
 310 
 311         /* if we haven't seen this filegrp yet */
 312         if (fgp == NULL) {
 313                 memset((char *)&fg, '\0', sizeof (fg));
 314                 fgp = &fg;
 315                 fgp->fg_magic = FG_MAGIC;
 316 
 317                 /* filegrp frontfile directory */
 318                 overhead += st->st_loghead.lh_maxbsize;
 319         }
 320 
 321         /* high-water the given count (if any) with our known count */
 322         if (count > fgp->fg_count)
 323                 fgp->fg_count = count;
 324 
 325         /* set a bit for this file */
 326         if ((gfileno / NBBY) < sizeof (fgp->fg_bits)) {
 327                 tbits = 1 << (gfileno % NBBY);
 328                 if (! (fgp->fg_bits[gfileno / NBBY] & tbits))
 329                         fgp->fg_bcount++;
 330                 fgp->fg_bits[gfileno / NBBY] |= tbits;
 331         }
 332 
 333         /* high-water our derived count with our known count */
 334         if (fgp->fg_bcount > fgp->fg_count)
 335                 fgp->fg_count = fgp->fg_bcount;
 336 
 337         /* account for the size of all known attrcache entries */
 338         size += fgp->fg_count * sizeof (struct cachefs_metadata);
 339 
 340         /* round to the ceiling block boundary */
 341         size += st->st_loghead.lh_maxbsize - 1;
 342         size &= ~ (st->st_loghead.lh_maxbsize - 1);
 343 
 344         /* sneaky :-) -- high-water fg_size, and make size the delta */
 345         size -= fgp->fg_size;
 346         fgp->fg_size += size;
 347 
 348         value.dptr = (char *)fgp;
 349         value.dsize = sizeof (*fgp);
 350         if (dbm_store(st->st_dbm, key, value, DBM_REPLACE) != 0)
 351                 stats_perror(st, SE_FILE,
 352                     gettext("Cannot store attrcache info"));
 353 
 354         return (size + overhead);
 355 }