Print this page
    
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/pcfs/pc_dir.c
          +++ new/usr/src/uts/common/fs/pcfs/pc_dir.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
  25   25   * Copyright 2016 Joyent, Inc.
  26   26   */
  27   27  
  28   28  #include <sys/param.h>
  29   29  #include <sys/errno.h>
  30   30  #include <sys/systm.h>
  31   31  #include <sys/sysmacros.h>
  32   32  #include <sys/buf.h>
  33   33  #include <sys/vfs.h>
  34   34  #include <sys/kmem.h>
  35   35  #include <sys/vnode.h>
  36   36  #include <sys/debug.h>
  37   37  #include <sys/cmn_err.h>
  38   38  #include <sys/sunddi.h>
  39   39  #include <sys/fs/pc_label.h>
  40   40  #include <sys/fs/pc_fs.h>
  41   41  #include <sys/fs/pc_dir.h>
  42   42  #include <sys/fs/pc_node.h>
  43   43  
  44   44  static int pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
  45   45      int ndirentries, struct vattr *vap, offset_t offset);
  46   46  static int pc_dirempty(struct pcnode *);
  47   47  static int pc_findentry(struct pcnode *, char *, struct pcslot *, offset_t *);
  48   48  static int pc_parsename(char *, char *, char *);
  49   49  static int pc_remove_long_fn(struct pcnode *pcp,
  50   50      offset_t lfn_offset);
  51   51  static int generate_short_name(struct pcnode *dp, char *namep,
  52   52      struct pcdir *ep);
  53   53  static struct pcdir *pc_name_to_pcdir(struct pcnode *dp, char *namep,
  54   54      int ndirentries, int *errret);
  55   55  static offset_t pc_find_free_space(struct pcnode *pcp, int ndirentries);
  56   56  static int direntries_needed(struct pcnode *dp, char *namep);
  57   57  static int pc_is_short_file_name(char *namep, int foldcase);
  58   58  static int shortname_exists(struct pcnode *dp, char *fname, char *fext);
  59   59  static int pc_dirfixdotdot(struct pcnode *cdp, struct pcnode *opdp,
  60   60      struct pcnode *npdp);
  61   61  /*
  62   62   * Tunables
  63   63   */
  64   64  int enable_long_filenames = 1;
  65   65  
  66   66  /*
  67   67   * Lookup a name in a directory. Return a pointer to the pc_node
  68   68   * which represents the entry.
  69   69   */
  70   70  int
  71   71  pc_dirlook(
  72   72          struct pcnode *dp,              /* parent directory */
  73   73          char *namep,                    /* name to lookup */
  74   74          struct pcnode **pcpp)           /* result */
  75   75  {
  76   76          struct vnode *vp;
  77   77          struct pcslot slot;
  78   78          int error;
  79   79  
  80   80          PC_DPRINTF2(4, "pc_dirlook (dp %p name %s)\n", (void *)dp, namep);
  81   81  
  82   82          if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
  83   83                  return (ENOTDIR);
  84   84          }
  85   85          vp = PCTOV(dp);
  86   86          /*
  87   87           * check now for changed disk, before any return(0)
  88   88           */
  89   89          if (error = pc_verify(VFSTOPCFS(vp->v_vfsp)))
  90   90                  return (error);
  91   91  
  92   92          /*
  93   93           * Null component name is synonym for directory being searched.
  94   94           */
  95   95          if (*namep == '\0') {
  96   96                  VN_HOLD(vp);
  97   97                  *pcpp = dp;
  98   98                  return (0);
  99   99          }
 100  100          /*
 101  101           * The root directory does not have "." and ".." entries,
 102  102           * so they are faked here.
 103  103           */
 104  104          if (vp->v_flag & VROOT) {
 105  105                  if (bcmp(namep, ".", 2) == 0 || bcmp(namep, "..", 3) == 0) {
 106  106                          VN_HOLD(vp);
 107  107                          *pcpp = dp;
 108  108                          return (0);
 109  109                  }
 110  110          }
 111  111          error = pc_findentry(dp, namep, &slot, NULL);
 112  112          if (error == 0) {
 113  113                  *pcpp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
 114  114                      slot.sl_blkno, slot.sl_offset, slot.sl_ep);
 115  115                  brelse(slot.sl_bp);
 116  116                  PC_DPRINTF1(4, "pc_dirlook: FOUND pcp=%p\n", (void *)*pcpp);
 117  117          } else if (error == EINVAL) {
 118  118                  error = ENOENT;
 119  119          }
 120  120          return (error);
 121  121  }
 122  122  
 123  123  /*
 124  124   * Enter a name in a directory.
 125  125   */
 126  126  int
 127  127  pc_direnter(
 128  128          struct pcnode *dp,              /* directory to make entry in */
 129  129          char *namep,                    /* name of entry */
 130  130          struct vattr *vap,              /* attributes of new entry */
 131  131          struct pcnode **pcpp)
 132  132  {
 133  133          int error;
 134  134          struct pcslot slot;
 135  135          struct vnode *vp = PCTOV(dp);
 136  136          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
 137  137          offset_t offset;
 138  138          daddr_t blkno;
 139  139          int     boff;
 140  140          struct buf *bp = NULL;
 141  141          struct pcdir *ep;
 142  142  
 143  143          PC_DPRINTF4(4, "pc_dirent(dp %p, name %s, vap %p, pcpp %p\n",
 144  144              (void *)dp, namep, (void *)vap, (void *)pcpp);
 145  145  
 146  146          if (pcpp != NULL)
 147  147                  *pcpp = NULL;
 148  148          /*
 149  149           * Leading spaces are not allowed in DOS.
 150  150           */
 151  151          if (*namep == ' ')
 152  152                  return (EINVAL);
 153  153          /*
 154  154           * If name is "." or "..", just look it up.
 155  155           */
 156  156          if (PC_NAME_IS_DOT(namep) || PC_NAME_IS_DOTDOT(namep)) {
 157  157                  if (pcpp) {
 158  158                          error = pc_dirlook(dp, namep, pcpp);
 159  159                          if (error)
 160  160                                  return (error);
 161  161                  }
 162  162                  return (EEXIST);
 163  163          }
 164  164          if (PCA_IS_HIDDEN(fsp, dp->pc_entry.pcd_attr)) {
 165  165                  return (EPERM);
 166  166          }
 167  167          /*
 168  168           * Make sure directory has not been removed while fs was unlocked.
 169  169           */
 170  170          if (dp->pc_entry.pcd_filename[0] == PCD_ERASED) {
 171  171                  return (ENOENT);
 172  172          }
 173  173          error = pc_findentry(dp, namep, &slot, NULL);
 174  174          if (error == 0) {
 175  175                  if (pcpp) {
 176  176                          *pcpp =
 177  177                              pc_getnode(fsp, slot.sl_blkno, slot.sl_offset,
 178  178                              slot.sl_ep);
 179  179                          error = EEXIST;
 180  180                  }
 181  181                  brelse(slot.sl_bp);
 182  182          } else if (error == ENOENT) {
 183  183                  struct pcdir *direntries;
 184  184                  int     ndirentries;
 185  185  
 186  186                  /*
 187  187                   * The entry does not exist. Check write permission in
 188  188                   * directory to see if entry can be created.
 189  189                   */
 190  190                  if (dp->pc_entry.pcd_attr & PCA_RDONLY) {
 191  191                          return (EPERM);
 192  192                  }
 193  193                  error = 0;
 194  194                  /*
 195  195                   * Make sure there is a slot.
 196  196                   */
 197  197                  if (slot.sl_status == SL_NONE)
 198  198                          panic("pc_direnter: no slot\n");
 199  199                  ndirentries = direntries_needed(dp, namep);
 200  200                  if (ndirentries == -1) {
 201  201                          return (EINVAL);
 202  202                  }
 203  203  
 204  204                  offset = pc_find_free_space(dp, ndirentries);
 205  205                  if (offset == -1) {
 206  206                          return (ENOSPC);
 207  207                  }
 208  208  
 209  209                  /*
 210  210                   * Make an entry from the supplied attributes.
 211  211                   */
 212  212                  direntries = pc_name_to_pcdir(dp, namep, ndirentries, &error);
 213  213                  if (direntries == NULL) {
 214  214                          return (error);
 215  215                  }
 216  216                  error = pc_makedirentry(dp, direntries, ndirentries, vap,
 217  217                      offset);
 218  218                  kmem_free(direntries, ndirentries * sizeof (struct pcdir));
 219  219                  if (error) {
 220  220                          return (error);
 221  221                  }
 222  222                  offset += (ndirentries - 1)  * sizeof (struct pcdir);
 223  223                  boff = pc_blkoff(fsp, offset);
 224  224                  error = pc_blkatoff(dp, offset, &bp, &ep);
 225  225                  if (error) {
 226  226                          return (error);
 227  227                  }
 228  228                  blkno = pc_daddrdb(fsp, bp->b_blkno);
 229  229                  /*
 230  230                   * Get a pcnode for the new entry.
 231  231                   */
 232  232                  *pcpp = pc_getnode(fsp, blkno, boff, ep);
 233  233                  brelse(bp);
 234  234                  if (vap->va_type == VDIR)
 235  235                          (*pcpp)->pc_size = fsp->pcfs_clsize;
 236  236  
 237  237                  /*
 238  238                   * Write out the new entry in the parent directory.
 239  239                   */
 240  240                  error = pc_syncfat(fsp);
 241  241                  if (!error) {
 242  242                          error = pc_nodeupdate(*pcpp);
 243  243                  }
 244  244          }
 245  245          return (error);
 246  246  }
 247  247  
 248  248  /*
 249  249   * Template for "." and ".." directory entries.
 250  250   */
 251  251  static struct {
 252  252          struct pcdir t_dot;             /* dot entry */
 253  253          struct pcdir t_dotdot;          /* dotdot entry */
 254  254  } dirtemplate = {
 255  255          {
 256  256                  ".       ",
 257  257                  "   ",
 258  258                  PCA_DIR
 259  259          },
 260  260          {
 261  261                  "..      ",
 262  262                  "   ",
 263  263                  PCA_DIR
 264  264          }
 265  265  };
 266  266  
 267  267  /*
 268  268   * Convert an attributes structure into the short filename entry
 269  269   * and write out the whole entry.
 270  270   */
 271  271  static int
 272  272  pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
 273  273      int ndirentries, struct vattr *vap, offset_t offset)
 274  274  {
 275  275          struct vnode *vp = PCTOV(dp);
 276  276          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
 277  277          int error;
 278  278          struct pcdir *ep;
 279  279          int     boff;
 280  280          int     i;
 281  281          struct buf *bp = NULL;
 282  282          timestruc_t now;
 283  283  
 284  284          if (vap != NULL && vap->va_mask & (AT_ATIME|AT_MTIME))
 285  285                  return (EOPNOTSUPP);
 286  286  
 287  287          ep = &direntries[ndirentries - 1];
 288  288          gethrestime(&now);
 289  289          if (error = pc_tvtopct(&now, &ep->pcd_mtime))
 290  290                  return (error);
 291  291  
 292  292          ep->pcd_crtime = ep->pcd_mtime;
 293  293          ep->pcd_ladate = ep->pcd_mtime.pct_date;
 294  294          ep->pcd_crtime_msec = 0;
 295  295          ep->pcd_size = 0;
 296  296          ep->pcd_attr = 0;
 297  297          /*
 298  298           * Fields we don't use.
 299  299           */
 300  300          ep->pcd_ntattr = 0;
 301  301          if (!IS_FAT32(fsp))
 302  302                  ep->un.pcd_eattr = 0;
 303  303  
 304  304          if (vap && ((vap->va_mode & 0222) == 0))
 305  305                  ep->pcd_attr |=  PCA_RDONLY;
 306  306          if (vap && (vap->va_type == VDIR)) {
 307  307                  pc_cluster32_t cn;
 308  308  
 309  309                  ep->pcd_attr |= PCA_DIR;
 310  310                  /*
 311  311                   * Make dot and dotdot entries for a new directory.
 312  312                   */
 313  313                  cn = pc_alloccluster(fsp, 0);
 314  314                  switch (cn) {
 315  315                  case PCF_FREECLUSTER:
 316  316                          return (ENOSPC);
 317  317                  case PCF_ERRORCLUSTER:
 318  318                          return (EIO);
 319  319                  }
 320  320                  bp = ngeteblk(fsp->pcfs_clsize);
 321  321                  bp->b_edev = fsp->pcfs_xdev;
 322  322                  bp->b_dev = cmpdev(bp->b_edev);
 323  323                  bp->b_blkno = pc_cldaddr(fsp, cn);
 324  324                  clrbuf(bp);
 325  325                  pc_setstartcluster(fsp, ep, cn);
 326  326                  pc_setstartcluster(fsp, &dirtemplate.t_dot, cn);
 327  327                  cn = pc_getstartcluster(fsp, &dp->pc_entry);
 328  328                  pc_setstartcluster(fsp, &dirtemplate.t_dotdot, cn);
 329  329                  dirtemplate.t_dot.pcd_mtime =
 330  330                      dirtemplate.t_dotdot.pcd_mtime = ep->pcd_mtime;
 331  331                  dirtemplate.t_dot.pcd_crtime =
 332  332                      dirtemplate.t_dotdot.pcd_crtime = ep->pcd_crtime;
 333  333                  dirtemplate.t_dot.pcd_ladate =
 334  334                      dirtemplate.t_dotdot.pcd_ladate = ep->pcd_ladate;
 335  335                  dirtemplate.t_dot.pcd_crtime_msec =
 336  336                      dirtemplate.t_dotdot.pcd_crtime_msec = 0;
 337  337                  bcopy(&dirtemplate,
 338  338                      bp->b_un.b_addr, sizeof (dirtemplate));
 339  339                  bwrite2(bp);
 340  340                  error = geterror(bp);
 341  341                  brelse(bp);
 342  342                  if (error) {
 343  343                          PC_DPRINTF0(1, "pc_makedirentry error");
 344  344                          pc_mark_irrecov(fsp);
 345  345                          return (EIO);
 346  346                  }
 347  347          } else {
 348  348                  pc_setstartcluster(fsp, ep, 0);
 349  349          }
 350  350          bp = NULL;
 351  351          for (i = 0, ep = NULL; i < ndirentries; i++, ep++) {
 352  352                  boff = pc_blkoff(fsp, offset);
 353  353                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
 354  354                          if (bp != NULL) {
 355  355                                  /* always modified */
 356  356                                  bwrite2(bp);
 357  357                                  error = geterror(bp);
 358  358                                  brelse(bp);
 359  359                                  if (error)
 360  360                                          return (error);
 361  361                                  bp = NULL;
 362  362                          }
 363  363                          error = pc_blkatoff(dp, offset, &bp, &ep);
 364  364                          if (error)
 365  365                                  return (error);
 366  366                  }
 367  367  
 368  368                  *ep = direntries[i];
 369  369                  offset += sizeof (struct pcdir);
 370  370          }
 371  371          if (bp != NULL) {
 372  372                  /* always modified */
 373  373                  bwrite2(bp);
 374  374                  error = geterror(bp);
 375  375                  brelse(bp);
 376  376                  if (error)
 377  377                          return (error);
 378  378          }
 379  379          return (0);
 380  380  }
 381  381  
 382  382  /*
 383  383   * Remove a name from a directory.
 384  384   */
 385  385  int
 386  386  pc_dirremove(
 387  387          struct pcnode *dp,
 388  388          char *namep,
 389  389          struct vnode *cdir,
 390  390          enum vtype type,
 391  391          caller_context_t *ctp)
 392  392  {
 393  393          struct pcslot slot;
 394  394          struct pcnode *pcp;
 395  395          int error;
 396  396          struct vnode *vp = PCTOV(dp);
 397  397          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
 398  398          offset_t lfn_offset = -1;
 399  399  
 400  400          PC_DPRINTF2(4, "pc_dirremove (dp %p name %s)\n", (void *)dp, namep);
 401  401          if ((dp->pc_entry.pcd_attr & PCA_RDONLY) ||
 402  402              PCA_IS_HIDDEN(fsp, dp->pc_entry.pcd_attr)) {
 403  403                  return (EPERM);
 404  404          }
 405  405          error = pc_findentry(dp, namep, &slot, &lfn_offset);
 406  406          if (error)
 407  407                  return (error);
 408  408          if (slot.sl_flags == SL_DOT) {
 409  409                  error = EINVAL;
 410  410          } else if (slot.sl_flags == SL_DOTDOT) {
 411  411                  error = ENOTEMPTY;
 412  412          } else {
 413  413                  pcp =
 414  414                      pc_getnode(VFSTOPCFS(vp->v_vfsp),
 415  415                      slot.sl_blkno, slot.sl_offset, slot.sl_ep);
 416  416          }
 417  417          if (error) {
 418  418                  brelse(slot.sl_bp);
 419  419                  return (error);
 420  420          }
 421  421          if (type == VDIR) {
 422  422                  if (pcp->pc_entry.pcd_attr & PCA_DIR) {
 423  423                          if (PCTOV(pcp) == cdir)
 424  424                                  error = EINVAL;
 425  425                          else if (!pc_dirempty(pcp))
 426  426                                  error = ENOTEMPTY;
 427  427                  } else {
 428  428                          error = ENOTDIR;
 429  429                  }
 430  430          } else {
 431  431                  if (pcp->pc_entry.pcd_attr & PCA_DIR)
 432  432                          error = EISDIR;
 433  433          }
 434  434          if (error == 0) {
 435  435                  /*
 436  436                   * Mark the in core node and on disk entry
 437  437                   * as removed. The slot may then be reused.
 438  438                   * The files clusters will be deallocated
 439  439                   * when the last reference goes away.
 440  440                   */
 441  441                  pcp->pc_eblkno = -1;
 442  442                  pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
 443  443                  if (lfn_offset != -1) {
 444  444                          brelse(slot.sl_bp);
 445  445                          error = pc_remove_long_fn(dp, lfn_offset);
 446  446                          if (error) {
 447  447                                  VN_RELE(PCTOV(pcp));
 448  448                                  pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
 449  449                                  return (EIO);
 450  450                          }
 451  451                  } else {
 452  452                          slot.sl_ep->pcd_filename[0] = PCD_ERASED;
 453  453                          bwrite2(slot.sl_bp);
 454  454                          error = geterror(slot.sl_bp);
 455  455                          brelse(slot.sl_bp);
 456  456                  }
 457  457                  if (error) {
 458  458                          VN_RELE(PCTOV(pcp));
 459  459                          pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
 460  460                          return (EIO);
 461  461                  } else if (type == VDIR) {
 462  462                          error = pc_truncate(pcp, 0L);
 463  463                  }
 464  464  
 465  465          } else {
 466  466                  brelse(slot.sl_bp);
 467  467          }
 468  468  
 469  469          if (error == 0) {
 470  470                  if (type == VDIR) {
 471  471                          vnevent_rmdir(PCTOV(pcp), vp, namep, ctp);
 472  472                  } else {
 473  473                          vnevent_remove(PCTOV(pcp), vp, namep, ctp);
 474  474                  }
 475  475          }
 476  476  
 477  477          VN_RELE(PCTOV(pcp));
 478  478  
 479  479          return (error);
 480  480  }
 481  481  
 482  482  /*
 483  483   * Determine whether a directory is empty.
 484  484   */
 485  485  static int
 486  486  pc_dirempty(struct pcnode *pcp)
 487  487  {
 488  488          struct buf *bp;
 489  489          struct pcdir *ep;
 490  490          offset_t offset;
 491  491          int boff;
 492  492          char c;
 493  493          int error;
 494  494          struct vnode *vp;
 495  495  
 496  496          vp = PCTOV(pcp);
 497  497          bp = NULL;
 498  498  
 499  499          offset = 0;
 500  500          for (;;) {
 501  501  
 502  502                  /*
 503  503                   * If offset is on a block boundary,
 504  504                   * read in the next directory block.
 505  505                   * Release previous if it exists.
 506  506                   */
 507  507                  boff = pc_blkoff(VFSTOPCFS(vp->v_vfsp), offset);
 508  508                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
 509  509                          if (bp != NULL)
 510  510                                  brelse(bp);
 511  511                          if (error = pc_blkatoff(pcp, offset, &bp, &ep)) {
 512  512                                  return (error);
 513  513                          }
 514  514                  }
 515  515                  if (PCDL_IS_LFN(ep)) {
 516  516                          error = pc_extract_long_fn(pcp, NULL, &ep, &offset,
 517  517                              &bp);
 518  518                          /*
 519  519                           * EINVAL means the lfn was invalid, so start with
 520  520                           * the next entry. Otherwise, an error occurred _or_
 521  521                           * the lfn is valid, either of which means the
 522  522                           * directory is not empty.
 523  523                           */
 524  524                          if (error == EINVAL)
 525  525                                  continue;
 526  526                          else {
 527  527                                  if (bp)
 528  528                                          brelse(bp);
 529  529                                  return (error);
 530  530                          }
 531  531                  }
 532  532                  c = ep->pcd_filename[0];
 533  533                  if (c == PCD_UNUSED)
 534  534                          break;
 535  535                  if ((c != '.') && (c != PCD_ERASED)) {
 536  536                          brelse(bp);
 537  537                          return (0);
 538  538                  }
 539  539                  if ((c == '.') && !PC_SHORTNAME_IS_DOT(ep->pcd_filename) &&
 540  540                      !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
 541  541                          brelse(bp);
 542  542                          return (0);
 543  543                  }
 544  544                  ep++;
 545  545                  offset += sizeof (struct pcdir);
 546  546          }
 547  547          if (bp != NULL)
 548  548                  brelse(bp);
 549  549          return (1);
 550  550  }
 551  551  
 552  552  /*
 553  553   * Rename a file.
 554  554   */
 555  555  int
 556  556  pc_rename(
 557  557          struct pcnode *dp,              /* parent directory */
 558  558          struct pcnode *tdp,             /* target directory */
 559  559          char *snm,                      /* source file name */
 560  560          char *tnm,                      /* target file name */
 561  561          caller_context_t *ctp)
 562  562  {
 563  563          struct pcnode *pcp;     /* pcnode we are trying to rename */
 564  564          struct pcnode *tpcp = NULL; /* pcnode that's in our way */
 565  565          struct pcslot slot;
 566  566          int error;
 567  567          struct vnode *vp = PCTOV(dp);
 568  568          struct vnode *svp = NULL;
 569  569          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
 570  570          int filecasechange = 0;
 571  571          int oldisdir = 0;
 572  572  
 573  573          PC_DPRINTF3(4, "pc_rename(0x%p, %s, %s)\n", (void *)dp, snm, tnm);
 574  574          /*
 575  575           * Leading spaces are not allowed in DOS.
 576  576           */
 577  577          if (*tnm == ' ')
 578  578                  return (EINVAL);
 579  579          /*
 580  580           * No dot or dotdot.
 581  581           */
 582  582          if (PC_NAME_IS_DOT(snm) || PC_NAME_IS_DOTDOT(snm) ||
 583  583              PC_NAME_IS_DOT(tnm) || PC_NAME_IS_DOTDOT(tnm))
 584  584                  return (EINVAL);
 585  585          /*
 586  586           * Get the source node.  We'll jump back to here if trying to
 587  587           * move on top of an existing file, after deleting that file.
 588  588           */
 589  589  top:
 590  590          error = pc_findentry(dp, snm, &slot, NULL);
 591  591          if (error) {
 592  592                  return (error);
 593  593          }
 594  594          pcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
 595  595              slot.sl_blkno, slot.sl_offset, slot.sl_ep);
 596  596  
 597  597          brelse(slot.sl_bp);
 598  598  
 599  599          if (pcp)
 600  600                  svp = PCTOV(pcp);
 601  601  
 602  602          /*
 603  603           * is the rename invalid, i.e. rename("a", "a/a")
 604  604           */
 605  605          if (pcp == tdp) {
 606  606                  if (svp)
 607  607                          VN_RELE(svp);
 608  608                  return (EINVAL);
 609  609          }
 610  610  
 611  611          /*
 612  612           * Are we just changing the case of an existing name?
 613  613           */
 614  614          if ((dp->pc_scluster == tdp->pc_scluster) &&
 615  615              (u8_strcmp(snm, tnm, 0, U8_STRCMP_CI_UPPER, U8_UNICODE_LATEST,
 616  616              &error) == 0)) {
 617  617                  filecasechange = 1;
 618  618          }
 619  619  
 620  620          /*
 621  621           * u8_strcmp detected an illegal character
 622  622           */
 623  623          if (error)
 624  624                  return (EINVAL);
 625  625  
 626  626          oldisdir = pcp->pc_entry.pcd_attr & PCA_DIR;
 627  627  
 628  628          /*
 629  629           * see if the target exists
 630  630           */
 631  631          error = pc_findentry(tdp, tnm, &slot, NULL);
 632  632          if (error == 0 && filecasechange == 0) {
 633  633                  /*
 634  634                   * Target exists.  If it's a file, delete it.  If it's
 635  635                   * a directory, bail.
 636  636                   */
 637  637                  int newisdir;
 638  638  
 639  639                  tpcp = pc_getnode(VFSTOPCFS(vp->v_vfsp),
 640  640                      slot.sl_blkno, slot.sl_offset, slot.sl_ep);
 641  641  
 642  642                  newisdir = tpcp->pc_entry.pcd_attr & PCA_DIR;
 643  643  
 644  644                  brelse(slot.sl_bp);
 645  645  
 646  646                  /*
 647  647                   * Error cases (from rename(2)):
 648  648                   * old is dir, new is dir: EEXIST
 649  649                   * old is dir, new is nondir: ENOTDIR
 650  650                   * old is nondir, new is dir: EISDIR
 651  651                   */
 652  652                  if (!newisdir) {
 653  653                          if (oldisdir) {
 654  654                                  error = ENOTDIR;
 655  655                          } else {
 656  656                                  /* nondir/nondir, remove target */
 657  657                                  error = pc_dirremove(tdp, tnm,
 658  658                                      (struct vnode *)NULL, VREG, ctp);
 659  659                                  if (error == 0) {
 660  660                                          vnevent_rename_dest(PCTOV(tpcp),
 661  661                                              PCTOV(tdp), tnm, ctp);
 662  662                                          VN_RELE(PCTOV(tpcp));
 663  663                                          tpcp = NULL;
 664  664                                          VN_RELE(PCTOV(pcp));
 665  665                                          goto top;
 666  666                                  }
 667  667                          }
 668  668                  } else if (oldisdir) {
 669  669                          /* dir/dir, remove target */
 670  670                          error = pc_dirremove(tdp, tnm,
 671  671                              (struct vnode *)NULL, VDIR, ctp);
 672  672                          if (error == 0) {
 673  673                                  vnevent_rename_dest(PCTOV(tpcp), PCTOV(tdp),
 674  674                                      tnm, ctp);
 675  675                                  VN_RELE(PCTOV(tpcp));
 676  676                                  tpcp = NULL;
 677  677                                  VN_RELE(PCTOV(pcp));
 678  678                                  goto top;
 679  679                          }
 680  680                          /* Follow rename(2)'s spec... */
 681  681                          if (error == ENOTEMPTY) {
 682  682                                  error = EEXIST;
 683  683                          }
 684  684                  } else {
 685  685                          /* nondir/dir, bail */
 686  686                          error = EISDIR;
 687  687                  }
 688  688          }
 689  689  
 690  690          if ((error == 0) || (error == ENOENT)) {
 691  691                  offset_t lfn_offset = -1;
 692  692                  daddr_t blkno;
 693  693                  struct pcdir *direntries;
 694  694                  struct pcdir *ep;
 695  695                  int     ndirentries;
 696  696                  pc_cluster16_t pct_lo;
 697  697                  pc_cluster16_t pct_hi;
 698  698                  offset_t offset;
 699  699                  int     boff;
 700  700                  struct buf *bp = NULL;
 701  701                  uchar_t attr;
 702  702                  int     size;
 703  703                  struct pctime mtime;
 704  704                  struct pctime crtime;
 705  705                  uchar_t ntattr;
 706  706                  ushort_t ladate;
 707  707                  ushort_t eattr;
 708  708                  uchar_t crtime_msec;
 709  709  
 710  710                  /*
 711  711                   * Rename the source.
 712  712                   */
 713  713                  /*
 714  714                   * Delete the old name, and create a new name.
 715  715                   */
 716  716                  if (filecasechange == 1 && error == 0)
 717  717                          brelse(slot.sl_bp);
 718  718                  ndirentries = direntries_needed(tdp, tnm);
 719  719                  if (ndirentries == -1) {
 720  720                          error = EINVAL;
 721  721                          goto done;
 722  722                  }
 723  723                  /*
 724  724                   * first see if we have enough space to create the new
 725  725                   * name before destroying the old one.
 726  726                   */
 727  727                  offset = pc_find_free_space(tdp, ndirentries);
 728  728                  if (offset == -1) {
 729  729                          error = ENOSPC;
 730  730                          goto done;
 731  731                  }
 732  732  
 733  733                  error = pc_findentry(dp, snm, &slot, &lfn_offset);
 734  734                  if (error) {
 735  735                          goto done;
 736  736                  }
 737  737                  pct_lo = slot.sl_ep->pcd_scluster_lo;
 738  738                  if (IS_FAT32(fsp))
 739  739                          pct_hi = slot.sl_ep->un.pcd_scluster_hi;
 740  740                  else
 741  741                          eattr = slot.sl_ep->un.pcd_eattr;
 742  742                  size = slot.sl_ep->pcd_size;
 743  743                  attr = slot.sl_ep->pcd_attr;
 744  744                  mtime = slot.sl_ep->pcd_mtime;
 745  745                  crtime = slot.sl_ep->pcd_crtime;
 746  746                  crtime_msec = slot.sl_ep->pcd_crtime_msec;
 747  747                  ntattr = slot.sl_ep->pcd_ntattr;
 748  748                  ladate = slot.sl_ep->pcd_ladate;
 749  749  
 750  750                  if (lfn_offset != -1) {
 751  751                          brelse(slot.sl_bp);
 752  752                          error = pc_remove_long_fn(dp, lfn_offset);
 753  753                          if (error) {
 754  754                                  pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
 755  755                                  goto done;
 756  756                          }
 757  757                  } else {
 758  758                          slot.sl_ep->pcd_filename[0] =
 759  759                              pcp->pc_entry.pcd_filename[0] = PCD_ERASED;
 760  760                          bwrite2(slot.sl_bp);
 761  761                          error = geterror(slot.sl_bp);
 762  762                          brelse(slot.sl_bp);
 763  763                  }
 764  764                  if (error) {
 765  765                          pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
 766  766                          error = EIO;
 767  767                          goto done;
 768  768                  }
 769  769  
 770  770                  /*
 771  771                   * Make an entry from the supplied attributes.
 772  772                   */
 773  773                  direntries = pc_name_to_pcdir(tdp, tnm, ndirentries, &error);
 774  774                  if (direntries == NULL) {
 775  775                          goto done;
 776  776                  }
 777  777  
 778  778                  error = pc_makedirentry(tdp, direntries, ndirentries, NULL,
 779  779                      offset);
 780  780                  kmem_free(direntries, ndirentries * sizeof (struct pcdir));
 781  781                  if (error) {
 782  782                          goto done;
 783  783                  }
 784  784                  /* advance to short name */
 785  785                  offset += (ndirentries - 1)  * sizeof (struct pcdir);
 786  786                  boff = pc_blkoff(fsp, offset);
 787  787                  error = pc_blkatoff(tdp, offset, &bp, &ep);
 788  788                  if (error) {
 789  789                          goto done;
 790  790                  }
 791  791                  blkno = pc_daddrdb(fsp, bp->b_blkno);
 792  792                  ep->pcd_scluster_lo = pct_lo;
 793  793                  if (IS_FAT32(fsp))
 794  794                          ep->un.pcd_scluster_hi = pct_hi;
 795  795                  else
 796  796                          ep->un.pcd_eattr = eattr;
 797  797                  ep->pcd_size = size;
 798  798                  ep->pcd_attr = attr;
 799  799                  ep->pcd_mtime = mtime;
 800  800                  ep->pcd_crtime = crtime;
 801  801                  ep->pcd_crtime_msec = crtime_msec;
 802  802                  ep->pcd_ntattr = ntattr;
 803  803                  ep->pcd_ladate = ladate;
 804  804                  bwrite2(bp);
 805  805                  error = geterror(bp);
 806  806                  pcp->pc_eblkno = blkno;
 807  807                  pcp->pc_eoffset = boff;
 808  808                  pcp->pc_entry = *ep;
 809  809                  pcp->pc_flags |= PC_CHG;
 810  810                  brelse(bp);
 811  811                  if (error) {
 812  812                          pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
 813  813                          error = EIO;
 814  814                          goto done;
 815  815                  }
 816  816                  /* No need to fix ".." if we're renaming within a dir */
 817  817                  if (oldisdir && dp != tdp) {
 818  818                          if ((error = pc_dirfixdotdot(pcp, dp, tdp)) != 0) {
 819  819                                  goto done;
 820  820                          }
 821  821                  }
 822  822                  if ((error = pc_nodeupdate(pcp)) != 0) {
 823  823                          goto done;
 824  824                  }
 825  825          }
 826  826  
 827  827          if (error == 0) {
 828  828                  vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm, ctp);
 829  829                  vnevent_rename_dest_dir(PCTOV(tdp), PCTOV(pcp), tnm, ctp);
 830  830          }
 831  831  
 832  832  done:
 833  833          if (tpcp != NULL)
 834  834                  VN_RELE(PCTOV(tpcp));
 835  835          VN_RELE(PCTOV(pcp));
 836  836  
 837  837          return (error);
 838  838  }
 839  839  
 840  840  /*
 841  841   * Fix the ".." entry of the child directory so that it points to the
 842  842   * new parent directory instead of the old one.
 843  843   */
 844  844  static int
 845  845  pc_dirfixdotdot(struct pcnode *dp,      /* child directory being moved */
 846  846          struct pcnode *opdp,            /* old parent directory */
 847  847          struct pcnode *npdp)            /* new parent directory */
 848  848  {
 849  849          pc_cluster32_t cn;
 850  850          struct vnode *vp = PCTOV(dp);
 851  851          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
 852  852          int error = 0;
 853  853          struct buf *bp = NULL;
 854  854          struct pcdir *ep = NULL;
 855  855          struct pcdir *tep = NULL;
 856  856  
 857  857          /*
 858  858           * set the new child's ".." directory entry starting cluster to
 859  859           * point to the new parent's starting cluster
 860  860           */
 861  861          ASSERT(opdp != npdp);
 862  862          error = pc_blkatoff(dp, (offset_t)0, &bp, &ep);
 863  863          if (error) {
 864  864                  PC_DPRINTF0(1, "pc_dirfixdotdot: error in blkatoff\n");
 865  865                  return (error);
 866  866          }
 867  867          tep = ep;
 868  868          ep++;
 869  869          if (!PC_SHORTNAME_IS_DOT(tep->pcd_filename) &&
 870  870              !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
 871  871                  PC_DPRINTF0(1, "pc_dirfixdotdot: mangled directory entry\n");
 872  872                  error = ENOTDIR;
 873  873                  return (error);
 874  874          }
 875  875          cn = pc_getstartcluster(fsp, &npdp->pc_entry);
 876  876          pc_setstartcluster(fsp, ep, cn);
 877  877  
 878  878          bwrite2(bp);
 879  879          error = geterror(bp);
 880  880          brelse(bp);
 881  881          if (error) {
 882  882                  PC_DPRINTF0(1, "pc_dirfixdotdot: error in write\n");
 883  883                  pc_mark_irrecov(fsp);
 884  884                  return (EIO);
 885  885          }
 886  886          return (0);
 887  887  }
 888  888  
 889  889  
 890  890  /*
 891  891   * Search a directory for an entry.
 892  892   * The directory should be locked as this routine
 893  893   * will sleep on I/O while searching.
 894  894   */
 895  895  static int
 896  896  pc_findentry(
 897  897          struct pcnode *dp,              /* parent directory */
 898  898          char *namep,                    /* name to lookup */
 899  899          struct pcslot *slotp,
 900  900          offset_t *lfn_offset)
 901  901  {
 902  902          offset_t offset;
 903  903          struct pcdir *ep = NULL;
 904  904          int boff;
 905  905          int error;
 906  906          struct vnode *vp;
 907  907          struct pcfs *fsp;
 908  908  
 909  909          vp = PCTOV(dp);
 910  910          PC_DPRINTF2(6, "pc_findentry: looking for %s in dir 0x%p\n", namep,
 911  911              (void *)dp);
 912  912          slotp->sl_status = SL_NONE;
 913  913          if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
 914  914                  return (ENOTDIR);
 915  915          }
 916  916          /*
 917  917           * Verify that the dp is still valid on the disk
 918  918           */
 919  919          fsp = VFSTOPCFS(vp->v_vfsp);
 920  920          error = pc_verify(fsp);
 921  921          if (error)
 922  922                  return (error);
 923  923  
 924  924          slotp->sl_bp = NULL;
 925  925          offset = 0;
 926  926          for (;;) {
 927  927                  /*
 928  928                   * If offset is on a block boundary,
 929  929                   * read in the next directory block.
 930  930                   * Release previous if it exists.
 931  931                   */
 932  932                  boff = pc_blkoff(fsp, offset);
 933  933                  if (boff == 0 || slotp->sl_bp == NULL ||
 934  934                      boff >= slotp->sl_bp->b_bcount) {
 935  935                          if (slotp->sl_bp != NULL) {
 936  936                                  brelse(slotp->sl_bp);
 937  937                                  slotp->sl_bp = NULL;
 938  938                          }
 939  939                          error = pc_blkatoff(dp, offset, &slotp->sl_bp, &ep);
 940  940                          if (error == ENOENT && slotp->sl_status == SL_NONE) {
 941  941                                  slotp->sl_status = SL_EXTEND;
 942  942                                  slotp->sl_offset = (int)offset;
 943  943                          }
 944  944                          if (error)
 945  945                                  return (error);
 946  946                  }
 947  947                  if ((ep->pcd_filename[0] == PCD_UNUSED) ||
 948  948                      (ep->pcd_filename[0] == PCD_ERASED)) {
 949  949                          /*
 950  950                           * note empty slots, in case name is not found
 951  951                           */
 952  952                          if (slotp->sl_status == SL_NONE) {
 953  953                                  slotp->sl_status = SL_FOUND;
 954  954                                  slotp->sl_blkno = pc_daddrdb(fsp,
 955  955                                      slotp->sl_bp->b_blkno);
 956  956                                  slotp->sl_offset = boff;
 957  957                          }
 958  958                          /*
 959  959                           * If unused we've hit the end of the directory
 960  960                           */
 961  961                          if (ep->pcd_filename[0] == PCD_UNUSED)
 962  962                                  break;
 963  963                          offset += sizeof (struct pcdir);
 964  964                          ep++;
 965  965                          continue;
 966  966                  }
 967  967                  if (PCDL_IS_LFN(ep)) {
 968  968                          offset_t t = offset;
 969  969                          if (pc_match_long_fn(dp, namep, &ep,
 970  970                              slotp, &offset) == 0) {
 971  971                                  if (lfn_offset != NULL)
 972  972                                          *lfn_offset = t;
 973  973                                  return (0);
 974  974                          }
 975  975                          continue;
 976  976                  }
 977  977                  if (pc_match_short_fn(dp, namep, &ep, slotp, &offset) == 0)
 978  978                          return (0);
 979  979          }
 980  980          if (slotp->sl_bp != NULL) {
 981  981                  brelse(slotp->sl_bp);
 982  982                  slotp->sl_bp = NULL;
 983  983          }
 984  984          return (ENOENT);
 985  985  }
 986  986  
 987  987  /*
 988  988   * Obtain the block at offset "offset" in file pcp.
 989  989   */
 990  990  int
 991  991  pc_blkatoff(
 992  992          struct pcnode *pcp,
 993  993          offset_t offset,
 994  994          struct buf **bpp,
 995  995          struct pcdir **epp)
 996  996  {
 997  997          struct pcfs *fsp;
 998  998          struct buf *bp;
 999  999          int size;
1000 1000          int error;
1001 1001          daddr_t bn;
1002 1002  
1003 1003          fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
1004 1004          size = pc_blksize(fsp, pcp, offset);
1005 1005          if (pc_blkoff(fsp, offset) >= size) {
1006 1006                  PC_DPRINTF0(5, "pc_blkatoff: ENOENT\n");
1007 1007                  return (ENOENT);
1008 1008          }
1009 1009          error = pc_bmap(pcp, pc_lblkno(fsp, offset), &bn, (uint_t *)0);
1010 1010          if (error)
1011 1011                  return (error);
1012 1012  
1013 1013          bp = bread(fsp->pcfs_xdev, bn, size);
1014 1014          if (bp->b_flags & B_ERROR) {
1015 1015                  PC_DPRINTF0(1, "pc_blkatoff: error\n");
1016 1016                  brelse(bp);
1017 1017                  pc_mark_irrecov(fsp);
1018 1018                  return (EIO);
1019 1019          }
1020 1020          if (epp) {
1021 1021                  *epp =
1022 1022                      (struct pcdir *)(bp->b_un.b_addr + pc_blkoff(fsp, offset));
1023 1023          }
1024 1024          *bpp = bp;
1025 1025          return (0);
1026 1026  }
1027 1027  
1028 1028  /*
1029 1029   * Parse user filename into the pc form of "filename.extension".
1030 1030   * If names are too long for the format (and enable_long_filenames is set)
1031 1031   * it returns EINVAL (since either this name was read from the disk (so
1032 1032   * it must fit), _or_ we're trying to match a long file name (so we
1033 1033   * should fail).  Tests for characters that are invalid in PCDOS and
1034 1034   * converts to upper case (unless foldcase is 0).
1035 1035   */
1036 1036  static int
1037 1037  pc_parsename(
1038 1038          char *namep,
1039 1039          char *fnamep,
1040 1040          char *fextp)
1041 1041  {
1042 1042          int n;
1043 1043          char c;
1044 1044  
1045 1045          n = PCFNAMESIZE;
1046 1046          c = *namep++;
1047 1047          if (c == 0)
1048 1048                  return (EINVAL);
1049 1049          if (c == '.') {
1050 1050                  /*
1051 1051                   * check for "." and "..".
1052 1052                   */
1053 1053                  *fnamep++ = c;
1054 1054                  n--;
1055 1055                  if (c = *namep++) {
1056 1056                          if ((c != '.') || (c = *namep)) /* ".x" or "..x" */
1057 1057                                  return (EINVAL);
1058 1058                          *fnamep++ = '.';
1059 1059                          n--;
1060 1060                  }
1061 1061          } else {
1062 1062                  /*
1063 1063                   * filename up to '.'
1064 1064                   */
1065 1065                  do {
1066 1066                          if (n-- > 0) {
1067 1067                                  c = toupper(c);
1068 1068                                  if (!pc_validchar(c))
1069 1069                                          return (EINVAL);
1070 1070                                  *fnamep++ = c;
1071 1071                          } else {
1072 1072                                  /* not short */
1073 1073                                  if (enable_long_filenames)
1074 1074                                          return (EINVAL);
1075 1075                          }
1076 1076                  } while ((c = *namep++) != '\0' && c != '.');
1077 1077          }
1078 1078          while (n-- > 0) {               /* fill with blanks */
1079 1079                  *fnamep++ = ' ';
1080 1080          }
1081 1081          /*
1082 1082           * remainder is extension
1083 1083           */
1084 1084          n = PCFEXTSIZE;
1085 1085          if (c == '.') {
1086 1086                  while ((c = *namep++) != '\0' && n--) {
1087 1087                          c = toupper(c);
1088 1088                          if (!pc_validchar(c))
1089 1089                                  return (EINVAL);
1090 1090                          *fextp++ = c;
1091 1091                  }
1092 1092                  if (enable_long_filenames && (c != '\0')) {
1093 1093                          /* not short */
1094 1094                          return (EINVAL);
1095 1095                  }
1096 1096          }
1097 1097          while (n-- > 0) {               /* fill with blanks */
1098 1098                  *fextp++ = ' ';
1099 1099          }
1100 1100          return (0);
1101 1101  }
1102 1102  
1103 1103  /*
1104 1104   * Match a long filename entry with 'namep'. Also return failure
1105 1105   * if the long filename isn't valid.
1106 1106   */
1107 1107  int
1108 1108  pc_match_long_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1109 1109      struct pcslot *slotp, offset_t *offset)
1110 1110  {
1111 1111          struct pcdir *ep = (struct pcdir *)*epp;
1112 1112          struct vnode *vp = PCTOV(pcp);
1113 1113          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1114 1114          int     error = 0;
1115 1115          char    lfn[PCMAXNAMLEN+1];
1116 1116  
1117 1117          error = pc_extract_long_fn(pcp, lfn, epp, offset, &slotp->sl_bp);
1118 1118          if (error) {
1119 1119                  if (error == EINVAL) {
1120 1120                          return (ENOENT);
1121 1121                  } else
1122 1122                          return (error);
1123 1123          }
1124 1124          ep = *epp;
1125 1125          if ((u8_strcmp(lfn, namep, 0, U8_STRCMP_CI_UPPER,
1126 1126              U8_UNICODE_LATEST, &error) == 0) && (error == 0)) {
1127 1127                  /* match */
1128 1128                  slotp->sl_flags = 0;
1129 1129                  slotp->sl_blkno = pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1130 1130                  slotp->sl_offset = pc_blkoff(fsp, *offset);
1131 1131                  slotp->sl_ep = ep;
1132 1132                  return (0);
1133 1133          }
1134 1134          *offset += sizeof (struct pcdir);
1135 1135          ep++;
1136 1136          *epp = ep;
1137 1137          /* If u8_strcmp detected an error it's sufficient to rtn ENOENT */
1138 1138          return (ENOENT);
1139 1139  }
1140 1140  
1141 1141  /*
1142 1142   * Match a short filename entry with namep.
1143 1143   */
1144 1144  int
1145 1145  pc_match_short_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1146 1146      struct pcslot *slotp, offset_t *offset)
1147 1147  {
1148 1148          char fname[PCFNAMESIZE];
1149 1149          char fext[PCFEXTSIZE];
1150 1150          struct pcdir *ep = *epp;
1151 1151          int     error;
1152 1152          struct vnode *vp = PCTOV(pcp);
1153 1153          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1154 1154          int boff = pc_blkoff(fsp, *offset);
1155 1155  
1156 1156          if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
1157 1157                  *offset += sizeof (struct pcdir);
1158 1158                  ep++;
1159 1159                  *epp = ep;
1160 1160                  return (ENOENT);
1161 1161          }
1162 1162  
1163 1163          error = pc_parsename(namep, fname, fext);
1164 1164          if (error) {
1165 1165                  *offset += sizeof (struct pcdir);
1166 1166                  ep++;
1167 1167                  *epp = ep;
1168 1168                  return (error);
1169 1169          }
1170 1170  
1171 1171          if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1172 1172              (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1173 1173                  /*
1174 1174                   * found the file
1175 1175                   */
1176 1176                  if (fname[0] == '.') {
1177 1177                          if (fname[1] == '.')
1178 1178                                  slotp->sl_flags = SL_DOTDOT;
1179 1179                          else
1180 1180                                  slotp->sl_flags = SL_DOT;
1181 1181                  } else {
1182 1182                          slotp->sl_flags = 0;
1183 1183                  }
1184 1184                  slotp->sl_blkno =
1185 1185                      pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1186 1186                  slotp->sl_offset = boff;
1187 1187                  slotp->sl_ep = ep;
1188 1188                  return (0);
1189 1189          }
1190 1190          *offset += sizeof (struct pcdir);
1191 1191          ep++;
1192 1192          *epp = ep;
1193 1193          return (ENOENT);
1194 1194  }
1195 1195  
1196 1196  /*
1197 1197   * Remove a long filename entry starting at lfn_offset. It must be
1198 1198   * a valid entry or we wouldn't have gotten here. Also remove the
1199 1199   * short filename entry.
1200 1200   */
1201 1201  static int
1202 1202  pc_remove_long_fn(struct pcnode *pcp, offset_t lfn_offset)
1203 1203  {
1204 1204          struct vnode *vp = PCTOV(pcp);
1205 1205          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1206 1206          int boff;
1207 1207          struct buf *bp = NULL;
1208 1208          struct pcdir *ep = NULL;
1209 1209          int     error = 0;
1210 1210  
1211 1211          /*
1212 1212           * if we're in here, we know that the lfn is in the proper format
1213 1213           * of <series-of-lfn-entries> followed by <sfn-entry>
1214 1214           */
1215 1215          for (;;) {
1216 1216                  boff = pc_blkoff(fsp, lfn_offset);
1217 1217                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1218 1218                          if (bp != NULL) {
1219 1219                                  bwrite2(bp);
1220 1220                                  error = geterror(bp);
1221 1221                                  brelse(bp);
1222 1222                                  if (error)
1223 1223                                          return (error);
1224 1224                                  bp = NULL;
1225 1225                          }
1226 1226                          error = pc_blkatoff(pcp, lfn_offset, &bp, &ep);
1227 1227                          if (error)
1228 1228                                  return (error);
1229 1229                  }
1230 1230                  if (!PCDL_IS_LFN(ep)) {
1231 1231                          /* done */
1232 1232                          break;
1233 1233                  }
1234 1234                  /* zap it */
1235 1235                  ep->pcd_filename[0] = PCD_ERASED;
1236 1236                  ep->pcd_attr = 0;
1237 1237                  lfn_offset += sizeof (struct pcdir);
1238 1238                  ep++;
1239 1239          }
1240 1240          /* now we're on the short entry */
1241 1241  
1242 1242          ep->pcd_filename[0] = PCD_ERASED;
1243 1243          ep->pcd_attr = 0;
1244 1244  
1245 1245          if (bp != NULL) {
1246 1246                  bwrite2(bp);
1247 1247                  error = geterror(bp);
1248 1248                  brelse(bp);
1249 1249                  if (error)
1250 1250                          return (error);
1251 1251          }
1252 1252          return (0);
1253 1253  }
1254 1254  
1255 1255  /*
1256 1256   * Find (and allocate) space in the directory denoted by
1257 1257   * 'pcp'. for 'ndirentries' pcdir structures.
1258 1258   * Return the offset at which to start, or -1 for failure.
1259 1259   */
1260 1260  static offset_t
1261 1261  pc_find_free_space(struct pcnode *pcp, int ndirentries)
1262 1262  {
1263 1263          offset_t offset = 0;
1264 1264          offset_t spaceneeded = ndirentries * sizeof (struct pcdir);
1265 1265          offset_t spaceoffset;
1266 1266          offset_t spaceavail = 0;
1267 1267          int boff;
1268 1268          struct buf *bp = NULL;
1269 1269          struct vnode *vp = PCTOV(pcp);
1270 1270          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1271 1271          struct pcdir *ep;
1272 1272          int     error;
1273 1273  
1274 1274          spaceoffset = offset;
1275 1275          while (spaceneeded > spaceavail) {
1276 1276                  /*
1277 1277                   * If offset is on a block boundary,
1278 1278                   * read in the next directory block.
1279 1279                   * Release previous if it exists.
1280 1280                   */
1281 1281                  boff = pc_blkoff(fsp, offset);
1282 1282                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1283 1283                          if (bp != NULL) {
1284 1284                                  brelse(bp);
1285 1285                                  bp = NULL;
1286 1286                          }
1287 1287                          error = pc_blkatoff(pcp, offset, &bp, &ep);
1288 1288                          if (error == ENOENT) {
1289 1289                                  daddr_t bn;
1290 1290  
1291 1291                                  /* extend directory */
1292 1292                                  if (!IS_FAT32(fsp) && (vp->v_flag & VROOT))
1293 1293                                          return (-1);
1294 1294                                  while (spaceneeded > spaceavail) {
1295 1295                                          error = pc_balloc(pcp,
1296 1296                                              pc_lblkno(fsp, offset), 1, &bn);
1297 1297                                          if (error)
1298 1298                                                  return (-1);
1299 1299                                          pcp->pc_size += fsp->pcfs_clsize;
1300 1300                                          spaceavail += fsp->pcfs_clsize;
1301 1301                                          offset += fsp->pcfs_clsize;
1302 1302                                  }
1303 1303                                  return (spaceoffset);
1304 1304                          }
1305 1305                          if (error)
1306 1306                                  return (-1);
1307 1307                  }
1308 1308                  if ((ep->pcd_filename[0] == PCD_UNUSED) ||
1309 1309                      (ep->pcd_filename[0] == PCD_ERASED)) {
1310 1310                          offset += sizeof (struct pcdir);
1311 1311                          spaceavail += sizeof (struct pcdir);
1312 1312                          ep++;
1313 1313                          continue;
1314 1314                  }
1315 1315                  offset += sizeof (struct pcdir);
1316 1316                  spaceavail = 0;
1317 1317                  spaceoffset = offset;
1318 1318                  ep++;
1319 1319          }
1320 1320          if (bp != NULL) {
1321 1321                  brelse(bp);
1322 1322          }
1323 1323          return (spaceoffset);
1324 1324  }
1325 1325  
1326 1326  /*
1327 1327   * Return how many long filename entries are needed.
1328 1328   * A maximum of PCLFNCHUNKSIZE characters per entry, plus one for a
1329 1329   * short filename.
1330 1330   */
1331 1331  static int
1332 1332  direntries_needed(struct pcnode *dp, char *namep)
1333 1333  {
1334 1334          struct pcdir ep;
1335 1335          uint16_t *w2_str;
1336 1336          size_t  u8l, u16l;
1337 1337          int ret;
1338 1338  
1339 1339          if (enable_long_filenames == 0) {
1340 1340                  return (1);
1341 1341          }
1342 1342          if (pc_is_short_file_name(namep, 0)) {
1343 1343                  (void) pc_parsename(namep, ep.pcd_filename, ep.pcd_ext);
1344 1344                  if (!shortname_exists(dp, ep.pcd_filename, ep.pcd_ext)) {
1345 1345                          return (1);
1346 1346                  }
1347 1347          }
1348 1348          if (pc_valid_long_fn(namep, 1)) {
1349 1349                  /*
1350 1350                   * convert to UTF-16 or UNICODE for calculating the entries
1351 1351                   * needed. Conversion will consume at the most 512 bytes
1352 1352                   */
1353 1353                  u16l = PCMAXNAMLEN + 1;
1354 1354                  w2_str = (uint16_t *)kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1355 1355                  u8l = strlen(namep);
1356 1356                  ret = uconv_u8tou16((const uchar_t *)namep, &u8l,
1357 1357                      w2_str, &u16l, UCONV_OUT_LITTLE_ENDIAN);
1358 1358                  kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1359 1359                  if (ret == 0) {
1360 1360                          ret = 1 + u16l / PCLFNCHUNKSIZE;
1361 1361                          if (u16l % PCLFNCHUNKSIZE != 0)
1362 1362                                  ret++;
1363 1363                          return (ret);
1364 1364                  }
1365 1365          }
1366 1366          return (-1);
1367 1367  }
1368 1368  
1369 1369  /*
1370 1370   * Allocate and return an array of pcdir structures for the passed-in
1371 1371   * name. ndirentries tells how many are required (including the short
1372 1372   * filename entry). Just allocate and fill them in properly here so they
1373 1373   * can be written out.
1374 1374   */
1375 1375  static struct pcdir *
1376 1376  pc_name_to_pcdir(struct pcnode *dp, char *namep, int ndirentries, int *errret)
1377 1377  {
1378 1378          struct pcdir *bpcdir;
1379 1379          struct pcdir *ep;
1380 1380          struct pcdir_lfn *lep;
1381 1381          int     i;
1382 1382          uchar_t cksum;
1383 1383          int     nchars;
1384 1384          int     error = 0;
1385 1385          char    *nameend;
1386 1386          uint16_t *w2_str;
1387 1387          size_t  u8l, u16l;
1388 1388          int ret;
1389 1389  
1390 1390          bpcdir = kmem_zalloc(ndirentries * sizeof (struct pcdir), KM_SLEEP);
1391 1391          ep = &bpcdir[ndirentries - 1];
1392 1392          if (ndirentries == 1) {
1393 1393                  (void) pc_parsename(namep, ep->pcd_filename, ep->pcd_ext);
1394 1394                  return (bpcdir);
1395 1395          }
1396 1396  
1397 1397          /* Here we need to convert to UTF-16 or UNICODE for writing */
1398 1398  
1399 1399          u16l = PCMAXNAMLEN + 1;
1400 1400          w2_str = (uint16_t *)kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1401 1401          u8l = strlen(namep);
1402 1402          ret = uconv_u8tou16((const uchar_t *)namep, &u8l, w2_str, &u16l,
1403 1403              UCONV_OUT_LITTLE_ENDIAN);
1404 1404          if (ret != 0) {
1405 1405                  kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1406 1406                  *errret = ret;
1407 1407                  return (NULL);
1408 1408          }
1409 1409          nameend = (char *)(w2_str + u16l);
1410 1410          u16l %= PCLFNCHUNKSIZE;
1411 1411          if (u16l != 0) {
1412 1412                  nchars = u16l + 1;
1413 1413                  nameend += 2;
1414 1414          } else {
1415 1415                  nchars = PCLFNCHUNKSIZE;
1416 1416          }
1417 1417          nchars *= sizeof (uint16_t);
1418 1418  
1419 1419          /* short file name */
1420 1420          error = generate_short_name(dp, namep, ep);
1421 1421          if (error) {
1422 1422                  kmem_free(bpcdir, ndirentries * sizeof (struct pcdir));
1423 1423                  *errret = error;
1424 1424                  return (NULL);
1425 1425          }
1426 1426          cksum = pc_checksum_long_fn(ep->pcd_filename, ep->pcd_ext);
1427 1427          for (i = 0; i < (ndirentries - 1); i++) {
1428 1428                  /* long file name */
1429 1429                  nameend -= nchars;
1430 1430                  lep = (struct pcdir_lfn *)&bpcdir[i];
1431 1431                  set_long_fn_chunk(lep, nameend, nchars);
1432 1432                  lep->pcdl_attr = PCDL_LFN_BITS;
1433 1433                  lep->pcdl_checksum = cksum;
1434 1434                  lep->pcdl_ordinal = (uchar_t)(ndirentries - i - 1);
1435 1435                  nchars = PCLFNCHUNKSIZE * sizeof (uint16_t);
1436 1436          }
1437 1437          kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1438 1438          lep = (struct pcdir_lfn *)&bpcdir[0];
1439 1439          lep->pcdl_ordinal |= 0x40;
1440 1440          return (bpcdir);
1441 1441  }
1442 1442  
1443 1443  static int
1444 1444  generate_short_name(struct pcnode *dp, char *namep, struct pcdir *inep)
1445 1445  {
1446 1446          int     rev;
1447 1447          int     nchars;
1448 1448          int     i, j;
1449 1449          char    *dot = NULL;
1450 1450          char    fname[PCFNAMESIZE+1];
1451 1451          char    fext[PCFEXTSIZE+1];
1452 1452          char    scratch[8];
1453 1453          int     error = 0;
1454 1454          struct  pcslot slot;
1455 1455          char    shortname[20];
1456 1456          int     force_tilde = 0;
1457 1457  
1458 1458          /*
1459 1459           * generate a unique short file name based on the long input name.
1460 1460           *
1461 1461           * Say, for "This is a very long filename.txt" generate
1462 1462           * "THISIS~1.TXT", or "THISIS~2.TXT" if that's already there.
1463 1463           * Skip invalid short name characters in the long name, plus
1464 1464           * a couple NT skips (space and reverse backslash).
1465 1465           *
1466 1466           * Unfortunately, since this name would be hidden by the normal
1467 1467           * lookup routine, we need to look for it ourselves. But luckily
1468 1468           * we don't need to look at the lfn entries themselves.
1469 1469           */
1470 1470          force_tilde = !pc_is_short_file_name(namep, 1);
1471 1471  
1472 1472          /*
1473 1473           * Strip off leading invalid characters.
1474 1474           * We need this because names like '.login' are now ok, but the
1475 1475           * short name needs to be something like LOGIN~1.
1476 1476           */
1477 1477          for (; *namep != '\0'; namep++) {
1478 1478                  if (*namep == ' ')
1479 1479                          continue;
1480 1480                  if (!pc_validchar(*namep) && !pc_validchar(toupper(*namep)))
1481 1481                          continue;
1482 1482                  break;
1483 1483          }
1484 1484          dot = strrchr(namep, '.');
1485 1485          if (dot != NULL) {
1486 1486                  dot++;
1487 1487                  for (j = 0, i = 0; j < PCFEXTSIZE; i++) {
1488 1488                          if (dot[i] == '\0')
1489 1489                                  break;
1490 1490                          /* skip valid, but not generally good characters */
1491 1491                          if (dot[i] == ' ' || dot[i] == '\\')
1492 1492                                  continue;
1493 1493                          if (pc_validchar(dot[i]))
1494 1494                                  fext[j++] = dot[i];
1495 1495                          else if (pc_validchar(toupper(dot[i])))
1496 1496                                  fext[j++] = toupper(dot[i]);
1497 1497                  }
1498 1498                  for (i = j; i < PCFEXTSIZE; i++)
1499 1499                          fext[i] = ' ';
1500 1500                  dot--;
1501 1501          } else {
1502 1502                  for (i = 0; i < PCFEXTSIZE; i++) {
1503 1503                          fext[i] = ' ';
1504 1504                  }
1505 1505          }
1506 1506          /*
1507 1507           * We know we're a long name, not a short name (or we wouldn't
1508 1508           * be here at all. But if uppercasing ourselves would be a short
1509 1509           * name, then we can possibly avoid the ~N format.
1510 1510           */
1511 1511          if (!force_tilde)
1512 1512                  rev = 0;
1513 1513          else
1514 1514                  rev = 1;
1515 1515          for (;;) {
1516 1516                  bzero(fname, sizeof (fname));
1517 1517                  nchars = PCFNAMESIZE;
1518 1518                  if (rev) {
1519 1519                          nchars--; /* ~ */
1520 1520                          i = rev;
1521 1521                          do {
1522 1522                                  nchars--;
1523 1523                                  i /= 10;
1524 1524                          } while (i);
1525 1525                          if (nchars <= 0) {
1526 1526                                  return (ENOSPC);
1527 1527                          }
1528 1528                  }
1529 1529                  for (j = 0, i = 0; j < nchars; i++) {
1530 1530                          if ((&namep[i] == dot) || (namep[i] == '\0'))
1531 1531                                  break;
1532 1532                          /* skip valid, but not generally good characters */
1533 1533                          if (namep[i] == ' ' || namep[i] == '\\')
1534 1534                                  continue;
1535 1535                          if (pc_validchar(namep[i]))
1536 1536                                  fname[j++] = namep[i];
1537 1537                          else if (pc_validchar(toupper(namep[i])))
1538 1538                                  fname[j++] = toupper(namep[i]);
1539 1539                  }
1540 1540                  if (rev) {
1541 1541                          (void) sprintf(scratch, "~%d", rev);
1542 1542                          (void) strcat(fname, scratch);
1543 1543                  }
1544 1544                  for (i = strlen(fname); i < PCFNAMESIZE; i++)
1545 1545                          fname[i] = ' ';
1546 1546                  /* now see if it exists */
1547 1547                  (void) pc_fname_ext_to_name(shortname, fname, fext, 0);
1548 1548                  error = pc_findentry(dp, shortname, &slot, NULL);
1549 1549                  if (error == 0) {
1550 1550                          /* found it */
1551 1551                          brelse(slot.sl_bp);
1552 1552                          rev++;
1553 1553                          continue;
1554 1554                  }
1555 1555                  if (!shortname_exists(dp, fname, fext))
1556 1556                          break;
1557 1557                  rev++;
1558 1558          }
1559 1559          (void) strncpy(inep->pcd_filename, fname, PCFNAMESIZE);
1560 1560          (void) strncpy(inep->pcd_ext, fext, PCFEXTSIZE);
1561 1561          return (0);
1562 1562  }
1563 1563  
1564 1564  /*
1565 1565   * Returns 1 if the passed-in filename is a short name, 0 if not.
1566 1566   */
1567 1567  static int
1568 1568  pc_is_short_file_name(char *namep, int foldcase)
1569 1569  {
1570 1570          int     i;
1571 1571          char    c;
1572 1572  
1573 1573          for (i = 0; i < PCFNAMESIZE; i++, namep++) {
1574 1574                  if (*namep == '\0')
1575 1575                          return (1);
1576 1576                  if (*namep == '.')
1577 1577                          break;
1578 1578                  if (foldcase)
1579 1579                          c = toupper(*namep);
1580 1580                  else
1581 1581                          c = *namep;
1582 1582                  if (!pc_validchar(c))
1583 1583                          return (0);
1584 1584          }
1585 1585          if (*namep == '\0')
1586 1586                  return (1);
1587 1587          if (*namep != '.')
1588 1588                  return (0);
1589 1589          namep++;
1590 1590          for (i = 0; i < PCFEXTSIZE; i++, namep++) {
1591 1591                  if (*namep == '\0')
1592 1592                          return (1);
1593 1593                  if (foldcase)
1594 1594                          c = toupper(*namep);
1595 1595                  else
1596 1596                          c = *namep;
1597 1597                  if (!pc_validchar(c))
1598 1598                          return (0);
1599 1599          }
1600 1600          /* we should be done. If not... */
1601 1601          if (*namep == '\0')
1602 1602                  return (1);
1603 1603          return (0);
1604 1604  
1605 1605  }
1606 1606  
1607 1607  /*
1608 1608   * We call this when we want to see if a short filename already exists
1609 1609   * in the filesystem as part of a long filename. When creating a short
1610 1610   * name (FILENAME.TXT from the user, or when generating one for a long
1611 1611   * filename), we cannot allow one that is part of a long filename.
1612 1612   * pc_findentry will find all the names that are visible (long or short),
1613 1613   * but will not crack any long filename entries.
1614 1614   */
1615 1615  static int
1616 1616  shortname_exists(struct pcnode *dp, char *fname, char *fext)
1617 1617  {
1618 1618          struct buf *bp = NULL;
1619 1619          int     offset = 0;
1620 1620          int     match = 0;
1621 1621          struct pcdir *ep;
1622 1622          struct vnode *vp = PCTOV(dp);
1623 1623          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1624 1624          int     boff;
1625 1625          int     error = 0;
1626 1626  
1627 1627          for (;;) {
1628 1628                  boff = pc_blkoff(fsp, offset);
1629 1629                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1630 1630                          if (bp != NULL) {
1631 1631                                  brelse(bp);
1632 1632                                  bp = NULL;
1633 1633                          }
1634 1634                          error = pc_blkatoff(dp, offset, &bp, &ep);
1635 1635                          if (error == ENOENT)
1636 1636                                  break;
1637 1637                          if (error) {
1638 1638                                  return (1);
1639 1639                          }
1640 1640                  }
1641 1641                  if (PCDL_IS_LFN(ep) ||
1642 1642                      (ep->pcd_filename[0] == PCD_ERASED)) {
1643 1643                          offset += sizeof (struct pcdir);
1644 1644                          ep++;
1645 1645                          continue;
1646 1646                  }
1647 1647                  if (ep->pcd_filename[0] == PCD_UNUSED)
1648 1648                          break;
1649 1649                  /*
1650 1650                   * in use, and a short file name (either standalone
1651 1651                   * or associated with a long name
1652 1652                   */
1653 1653                  if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1654 1654                      (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1655 1655                          match = 1;
1656 1656                          break;
1657 1657                  }
1658 1658                  offset += sizeof (struct pcdir);
1659 1659                  ep++;
1660 1660          }
1661 1661          if (bp) {
1662 1662                  brelse(bp);
1663 1663                  bp = NULL;
1664 1664          }
1665 1665          return (match);
1666 1666  }
1667 1667  
1668 1668  pc_cluster32_t
1669 1669  pc_getstartcluster(struct pcfs *fsp, struct pcdir *ep)
1670 1670  {
1671 1671          if (IS_FAT32(fsp)) {
1672 1672                  pc_cluster32_t cn;
1673 1673                  pc_cluster16_t hi16;
1674 1674                  pc_cluster16_t lo16;
1675 1675  
1676 1676                  hi16 = ltohs(ep->un.pcd_scluster_hi);
1677 1677                  lo16 = ltohs(ep->pcd_scluster_lo);
1678 1678                  cn = (hi16 << 16) | lo16;
1679 1679                  return (cn);
1680 1680          } else {
1681 1681                  return (ltohs(ep->pcd_scluster_lo));
1682 1682          }
1683 1683  }
1684 1684  
1685 1685  void
1686 1686  pc_setstartcluster(struct pcfs *fsp, struct pcdir *ep, pc_cluster32_t cln)
1687 1687  {
1688 1688          if (IS_FAT32(fsp)) {
1689 1689                  pc_cluster16_t hi16;
1690 1690                  pc_cluster16_t lo16;
1691 1691  
1692 1692                  hi16 = (cln >> 16) & 0xFFFF;
1693 1693                  lo16 = cln & 0xFFFF;
1694 1694                  ep->un.pcd_scluster_hi = htols(hi16);
1695 1695                  ep->pcd_scluster_lo = htols(lo16);
1696 1696          } else {
1697 1697                  pc_cluster16_t cln16;
1698 1698  
1699 1699                  cln16 = (pc_cluster16_t)cln;
1700 1700                  ep->pcd_scluster_lo = htols(cln16);
1701 1701          }
1702 1702  }
  
    | 
      ↓ open down ↓ | 
    1702 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX