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 1996-2002 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <sys/types.h>
  30 #include <stdio.h>
  31 #include <stddef.h>
  32 #include <sys/param.h>
  33 #include <sys/stat.h>
  34 #include <fcntl.h>
  35 #include <unistd.h>
  36 #include <errno.h>
  37 
  38 #include <sys/fs/cachefs_fs.h>
  39 #include <sys/fs/cachefs_dlog.h>
  40 
  41 /* forward references */
  42 static int create_mapfile(char *fname, int size);
  43 
  44 int
  45 dlog_ck(char *dir_path, ino64_t *maxlocalfilenop)
  46 {
  47         int err;
  48         int n;
  49         char dlog_path[MAXPATHLEN];
  50         char dmap_path[MAXPATHLEN];
  51         struct stat64 statinfo;
  52         int fd;
  53         int dlog_version;
  54         off_t offset;
  55         struct cfs_dlog_entry buf;
  56         int max_seq_num;
  57         int ent_count = 0;
  58         ino64_t fileno, maxlocalfileno;
  59 
  60         if (maxlocalfilenop)
  61                 *maxlocalfilenop = 0LL;
  62 
  63         n = strlen(dir_path) + strlen(CACHEFS_DLOG_FILE) + 2;
  64         if (n > MAXPATHLEN) {
  65                 pr_err(gettext("%s/%s: path too long"),
  66                     dir_path, CACHEFS_DLOG_FILE);
  67                 return (-1);
  68         }
  69         sprintf(dlog_path, "%s/%s", dir_path, CACHEFS_DLOG_FILE);
  70 
  71         n = strlen(dir_path) + strlen(CACHEFS_DMAP_FILE) + 2;
  72         if (n > MAXPATHLEN) {
  73                 pr_err(gettext("%s/%s: path too long"),
  74                     dir_path, CACHEFS_DMAP_FILE);
  75                 return (-1);
  76         }
  77         sprintf(dmap_path, "%s/%s", dir_path, CACHEFS_DMAP_FILE);
  78 
  79         err = lstat64(dlog_path, &statinfo);
  80         if (err < 0) {
  81                 if (errno == ENOENT)
  82                         (void) unlink(dmap_path);
  83                 /*
  84                  * No disconnect log(dlog) file exists to check
  85                  */
  86                 return (0);
  87         }
  88 
  89         /* this file will be <2GB */
  90         fd = open(dlog_path, O_RDWR);
  91         if (fd < 0) {
  92                 pr_err(gettext("can't open %s"), dlog_path);
  93                 return (-2);
  94         }
  95         err = read(fd, &dlog_version, sizeof (dlog_version));
  96         if (err != sizeof (dlog_version)) {
  97                 pr_err(gettext("can't read %s"), dlog_path);
  98                 (void) close(fd);
  99                 return (-3);
 100         }
 101         if (dlog_version != CFS_DLOG_VERSION) {
 102                 pr_err(gettext(
 103                     "unknown version number in %s"), dlog_path);
 104                 (void) close(fd);
 105                 return (-4);
 106         }
 107 
 108         offset = sizeof (dlog_version);
 109         max_seq_num = 0;
 110         maxlocalfileno = 0LL;
 111         while (offset < (off_t)statinfo.st_size) {
 112                 err = (int) lseek(fd, offset, SEEK_SET);
 113                 if (err == -1) {
 114                         pr_err(gettext("can't lseek %s"), dlog_path);
 115                         (void) close(fd);
 116                         return (-5);
 117                 }
 118 
 119                 err = read(fd, &buf, sizeof (buf));
 120                 if (err < 0) {
 121                         pr_err(gettext("can't read %s"), dlog_path);
 122                         (void) close(fd);
 123                         return (-6);
 124                 }
 125                 ++ent_count;
 126                 if (buf.dl_op ==  CFS_DLOG_TRAILER) {
 127                         goto out;
 128                 }
 129                 if ((buf.dl_len & 3) == 0) {
 130                         /*
 131                          * Record length must be on a word boundary and
 132                          * fit into the correct size range.
 133                          */
 134                         if ((buf.dl_len < sizeof (int)) ||
 135                             (buf.dl_len > CFS_DLOG_ENTRY_MAXSIZE)) {
 136                                 goto out;
 137                         }
 138                         /*
 139                          * Make sure length does not point beyond end of
 140                          * file
 141                          */
 142                         if ((offset + (off_t)buf.dl_len) >
 143                             (off_t)statinfo.st_size) {
 144                                 goto out;
 145                         }
 146                 } else {
 147                         goto out;
 148                 }
 149 
 150                 /* make sure the valid field is reasonable */
 151                 switch (buf.dl_valid) {
 152                 case CFS_DLOG_VAL_CRASH:
 153                 case CFS_DLOG_VAL_COMMITTED:
 154                 case CFS_DLOG_VAL_ERROR:
 155                 case CFS_DLOG_VAL_PROCESSED:
 156                         break;
 157                 default:
 158                         goto out;
 159                 }
 160 
 161                 /* make sure the operation field is reasonable */
 162                 fileno = 0LL;
 163                 switch (buf.dl_op) {
 164                 case CFS_DLOG_CREATE:
 165                         fileno = buf.dl_u.dl_create.dl_new_cid.cid_fileno;
 166                         break;
 167                 case CFS_DLOG_REMOVE:
 168                         break;
 169                 case CFS_DLOG_LINK:
 170                         break;
 171                 case CFS_DLOG_RENAME:
 172                         break;
 173                 case CFS_DLOG_MKDIR:
 174                         fileno = buf.dl_u.dl_mkdir.dl_child_cid.cid_fileno;
 175                         break;
 176                 case CFS_DLOG_RMDIR:
 177                         break;
 178                 case CFS_DLOG_SYMLINK:
 179                         fileno = buf.dl_u.dl_symlink.dl_child_cid.cid_fileno;
 180                         break;
 181                 case CFS_DLOG_SETATTR:
 182                         break;
 183                 case CFS_DLOG_SETSECATTR:
 184                         break;
 185                 case CFS_DLOG_MODIFIED:
 186                         break;
 187                 case CFS_DLOG_MAPFID:
 188                         break;
 189                 default:
 190                         goto out;
 191                 }
 192 
 193                 /* track the largest local fileno used */
 194                 if (maxlocalfileno < fileno)
 195                         maxlocalfileno = fileno;
 196 
 197                 /* track the largest sequence number used */
 198                 if (max_seq_num < buf.dl_seq) {
 199                         max_seq_num = buf.dl_seq;
 200                 }
 201 
 202                 offset += buf.dl_len;
 203         }
 204 
 205 out:
 206         if ((buf.dl_op != CFS_DLOG_TRAILER) ||
 207             (buf.dl_len != sizeof (struct cfs_dlog_trailer)) ||
 208             (buf.dl_valid != CFS_DLOG_VAL_COMMITTED) ||
 209             ((offset + (off_t)buf.dl_len) != (off_t)statinfo.st_size)) {
 210                 ftruncate(fd, offset);
 211                 buf.dl_len = sizeof (struct cfs_dlog_trailer);
 212                 buf.dl_op = CFS_DLOG_TRAILER;
 213                 buf.dl_valid = CFS_DLOG_VAL_COMMITTED;
 214                 buf.dl_seq = max_seq_num + 1;
 215                 if (wrdlog(fd, &buf,  buf.dl_len, offset) != 0) {
 216                         (void) close(fd);
 217                         return (-7);
 218                 }
 219         }
 220 
 221         if (fsync(fd) == -1) {
 222                 pr_err(gettext("Cannot sync %s"), dlog_path);
 223                 (void) close(fd);
 224                 return (-8);
 225         }
 226         (void) close(fd); /* ignore return since fsync() successful */
 227 
 228         /* check to see that mapfile exists; if not, create it. */
 229         if (access(dmap_path, F_OK) != 0) {
 230                 /* XXX ent_count is a very high upper bound */
 231                 if (create_mapfile(dmap_path,
 232                     ent_count * sizeof (struct cfs_dlog_mapping_space)) != 0) {
 233                         return (-9);
 234                 }
 235         }
 236 
 237         if (maxlocalfilenop)
 238                 *maxlocalfilenop = maxlocalfileno;
 239         return (0);
 240 }
 241 
 242 int
 243 wrdlog(int fd, char * buf, int len, off_t offset)
 244 {
 245         int err;
 246 
 247         err = lseek(fd, offset, SEEK_SET);
 248         if (err < 0) {
 249                 return (-1);
 250         }
 251 
 252         err = write(fd, buf, len);
 253         if (err != len) {
 254                 return (-2);
 255         }
 256 
 257         return (0);
 258 }
 259 
 260 static int
 261 create_mapfile(char *fname, int size)
 262 {
 263         char buffy[BUFSIZ];
 264         int fd, rc, wsize;
 265 
 266         /* this file will be <2GB */
 267         fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
 268         if (fd < 0)
 269                 return (errno);
 270 
 271         memset(buffy, '\0', sizeof (buffy));
 272         while (size > 0) {
 273                 wsize = (size > sizeof (buffy)) ? sizeof (buffy) : size;
 274                 if (write(fd, buffy, wsize) != wsize) {
 275                         rc = errno;
 276                         (void) close(fd);
 277                         (void) unlink(fname);
 278                         return (rc);
 279                 }
 280                 size -= wsize;
 281         }
 282 
 283         if (fsync(fd) != 0) {
 284                 rc = errno;
 285                 (void) close(fd);
 286                 (void) unlink(fname);
 287                 return (rc);
 288         }
 289         (void) close(fd);
 290 
 291         return (0);
 292 }