Print this page
    
OS-5291 lxbrand inotify02 LTP regression
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
OS-3294 add support for inotify
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
    
      
        | 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.
  
    | 
      ↓ open down ↓ | 
    14 lines elided | 
    
      ↑ open up ↑ | 
  
  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      - * Copyright 2015 Joyent, Inc.
       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) {
  
    | 
      ↓ open down ↓ | 
    783 lines elided | 
    
      ↑ open up ↑ | 
  
 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      -                if (dp != tdp)
 830      -                        vnevent_rename_dest_dir(PCTOV(tdp), ctp);
      829 +                vnevent_rename_dest_dir(PCTOV(tdp), PCTOV(pcp), tnm, ctp);
 831  830          }
 832  831  
 833  832  done:
 834  833          if (tpcp != NULL)
 835  834                  VN_RELE(PCTOV(tpcp));
 836  835          VN_RELE(PCTOV(pcp));
 837  836  
 838  837          return (error);
 839  838  }
 840  839  
 841  840  /*
 842  841   * Fix the ".." entry of the child directory so that it points to the
 843  842   * new parent directory instead of the old one.
 844  843   */
 845  844  static int
 846  845  pc_dirfixdotdot(struct pcnode *dp,      /* child directory being moved */
 847  846          struct pcnode *opdp,            /* old parent directory */
 848  847          struct pcnode *npdp)            /* new parent directory */
 849  848  {
 850  849          pc_cluster32_t cn;
 851  850          struct vnode *vp = PCTOV(dp);
 852  851          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
 853  852          int error = 0;
 854  853          struct buf *bp = NULL;
 855  854          struct pcdir *ep = NULL;
 856  855          struct pcdir *tep = NULL;
 857  856  
 858  857          /*
 859  858           * set the new child's ".." directory entry starting cluster to
 860  859           * point to the new parent's starting cluster
 861  860           */
 862  861          ASSERT(opdp != npdp);
 863  862          error = pc_blkatoff(dp, (offset_t)0, &bp, &ep);
 864  863          if (error) {
 865  864                  PC_DPRINTF0(1, "pc_dirfixdotdot: error in blkatoff\n");
 866  865                  return (error);
 867  866          }
 868  867          tep = ep;
 869  868          ep++;
 870  869          if (!PC_SHORTNAME_IS_DOT(tep->pcd_filename) &&
 871  870              !PC_SHORTNAME_IS_DOTDOT(ep->pcd_filename)) {
 872  871                  PC_DPRINTF0(1, "pc_dirfixdotdot: mangled directory entry\n");
 873  872                  error = ENOTDIR;
 874  873                  return (error);
 875  874          }
 876  875          cn = pc_getstartcluster(fsp, &npdp->pc_entry);
 877  876          pc_setstartcluster(fsp, ep, cn);
 878  877  
 879  878          bwrite2(bp);
 880  879          error = geterror(bp);
 881  880          brelse(bp);
 882  881          if (error) {
 883  882                  PC_DPRINTF0(1, "pc_dirfixdotdot: error in write\n");
 884  883                  pc_mark_irrecov(fsp);
 885  884                  return (EIO);
 886  885          }
 887  886          return (0);
 888  887  }
 889  888  
 890  889  
 891  890  /*
 892  891   * Search a directory for an entry.
 893  892   * The directory should be locked as this routine
 894  893   * will sleep on I/O while searching.
 895  894   */
 896  895  static int
 897  896  pc_findentry(
 898  897          struct pcnode *dp,              /* parent directory */
 899  898          char *namep,                    /* name to lookup */
 900  899          struct pcslot *slotp,
 901  900          offset_t *lfn_offset)
 902  901  {
 903  902          offset_t offset;
 904  903          struct pcdir *ep = NULL;
 905  904          int boff;
 906  905          int error;
 907  906          struct vnode *vp;
 908  907          struct pcfs *fsp;
 909  908  
 910  909          vp = PCTOV(dp);
 911  910          PC_DPRINTF2(6, "pc_findentry: looking for %s in dir 0x%p\n", namep,
 912  911              (void *)dp);
 913  912          slotp->sl_status = SL_NONE;
 914  913          if (!(dp->pc_entry.pcd_attr & PCA_DIR)) {
 915  914                  return (ENOTDIR);
 916  915          }
 917  916          /*
 918  917           * Verify that the dp is still valid on the disk
 919  918           */
 920  919          fsp = VFSTOPCFS(vp->v_vfsp);
 921  920          error = pc_verify(fsp);
 922  921          if (error)
 923  922                  return (error);
 924  923  
 925  924          slotp->sl_bp = NULL;
 926  925          offset = 0;
 927  926          for (;;) {
 928  927                  /*
 929  928                   * If offset is on a block boundary,
 930  929                   * read in the next directory block.
 931  930                   * Release previous if it exists.
 932  931                   */
 933  932                  boff = pc_blkoff(fsp, offset);
 934  933                  if (boff == 0 || slotp->sl_bp == NULL ||
 935  934                      boff >= slotp->sl_bp->b_bcount) {
 936  935                          if (slotp->sl_bp != NULL) {
 937  936                                  brelse(slotp->sl_bp);
 938  937                                  slotp->sl_bp = NULL;
 939  938                          }
 940  939                          error = pc_blkatoff(dp, offset, &slotp->sl_bp, &ep);
 941  940                          if (error == ENOENT && slotp->sl_status == SL_NONE) {
 942  941                                  slotp->sl_status = SL_EXTEND;
 943  942                                  slotp->sl_offset = (int)offset;
 944  943                          }
 945  944                          if (error)
 946  945                                  return (error);
 947  946                  }
 948  947                  if ((ep->pcd_filename[0] == PCD_UNUSED) ||
 949  948                      (ep->pcd_filename[0] == PCD_ERASED)) {
 950  949                          /*
 951  950                           * note empty slots, in case name is not found
 952  951                           */
 953  952                          if (slotp->sl_status == SL_NONE) {
 954  953                                  slotp->sl_status = SL_FOUND;
 955  954                                  slotp->sl_blkno = pc_daddrdb(fsp,
 956  955                                      slotp->sl_bp->b_blkno);
 957  956                                  slotp->sl_offset = boff;
 958  957                          }
 959  958                          /*
 960  959                           * If unused we've hit the end of the directory
 961  960                           */
 962  961                          if (ep->pcd_filename[0] == PCD_UNUSED)
 963  962                                  break;
 964  963                          offset += sizeof (struct pcdir);
 965  964                          ep++;
 966  965                          continue;
 967  966                  }
 968  967                  if (PCDL_IS_LFN(ep)) {
 969  968                          offset_t t = offset;
 970  969                          if (pc_match_long_fn(dp, namep, &ep,
 971  970                              slotp, &offset) == 0) {
 972  971                                  if (lfn_offset != NULL)
 973  972                                          *lfn_offset = t;
 974  973                                  return (0);
 975  974                          }
 976  975                          continue;
 977  976                  }
 978  977                  if (pc_match_short_fn(dp, namep, &ep, slotp, &offset) == 0)
 979  978                          return (0);
 980  979          }
 981  980          if (slotp->sl_bp != NULL) {
 982  981                  brelse(slotp->sl_bp);
 983  982                  slotp->sl_bp = NULL;
 984  983          }
 985  984          return (ENOENT);
 986  985  }
 987  986  
 988  987  /*
 989  988   * Obtain the block at offset "offset" in file pcp.
 990  989   */
 991  990  int
 992  991  pc_blkatoff(
 993  992          struct pcnode *pcp,
 994  993          offset_t offset,
 995  994          struct buf **bpp,
 996  995          struct pcdir **epp)
 997  996  {
 998  997          struct pcfs *fsp;
 999  998          struct buf *bp;
1000  999          int size;
1001 1000          int error;
1002 1001          daddr_t bn;
1003 1002  
1004 1003          fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
1005 1004          size = pc_blksize(fsp, pcp, offset);
1006 1005          if (pc_blkoff(fsp, offset) >= size) {
1007 1006                  PC_DPRINTF0(5, "pc_blkatoff: ENOENT\n");
1008 1007                  return (ENOENT);
1009 1008          }
1010 1009          error = pc_bmap(pcp, pc_lblkno(fsp, offset), &bn, (uint_t *)0);
1011 1010          if (error)
1012 1011                  return (error);
1013 1012  
1014 1013          bp = bread(fsp->pcfs_xdev, bn, size);
1015 1014          if (bp->b_flags & B_ERROR) {
1016 1015                  PC_DPRINTF0(1, "pc_blkatoff: error\n");
1017 1016                  brelse(bp);
1018 1017                  pc_mark_irrecov(fsp);
1019 1018                  return (EIO);
1020 1019          }
1021 1020          if (epp) {
1022 1021                  *epp =
1023 1022                      (struct pcdir *)(bp->b_un.b_addr + pc_blkoff(fsp, offset));
1024 1023          }
1025 1024          *bpp = bp;
1026 1025          return (0);
1027 1026  }
1028 1027  
1029 1028  /*
1030 1029   * Parse user filename into the pc form of "filename.extension".
1031 1030   * If names are too long for the format (and enable_long_filenames is set)
1032 1031   * it returns EINVAL (since either this name was read from the disk (so
1033 1032   * it must fit), _or_ we're trying to match a long file name (so we
1034 1033   * should fail).  Tests for characters that are invalid in PCDOS and
1035 1034   * converts to upper case (unless foldcase is 0).
1036 1035   */
1037 1036  static int
1038 1037  pc_parsename(
1039 1038          char *namep,
1040 1039          char *fnamep,
1041 1040          char *fextp)
1042 1041  {
1043 1042          int n;
1044 1043          char c;
1045 1044  
1046 1045          n = PCFNAMESIZE;
1047 1046          c = *namep++;
1048 1047          if (c == 0)
1049 1048                  return (EINVAL);
1050 1049          if (c == '.') {
1051 1050                  /*
1052 1051                   * check for "." and "..".
1053 1052                   */
1054 1053                  *fnamep++ = c;
1055 1054                  n--;
1056 1055                  if (c = *namep++) {
1057 1056                          if ((c != '.') || (c = *namep)) /* ".x" or "..x" */
1058 1057                                  return (EINVAL);
1059 1058                          *fnamep++ = '.';
1060 1059                          n--;
1061 1060                  }
1062 1061          } else {
1063 1062                  /*
1064 1063                   * filename up to '.'
1065 1064                   */
1066 1065                  do {
1067 1066                          if (n-- > 0) {
1068 1067                                  c = toupper(c);
1069 1068                                  if (!pc_validchar(c))
1070 1069                                          return (EINVAL);
1071 1070                                  *fnamep++ = c;
1072 1071                          } else {
1073 1072                                  /* not short */
1074 1073                                  if (enable_long_filenames)
1075 1074                                          return (EINVAL);
1076 1075                          }
1077 1076                  } while ((c = *namep++) != '\0' && c != '.');
1078 1077          }
1079 1078          while (n-- > 0) {               /* fill with blanks */
1080 1079                  *fnamep++ = ' ';
1081 1080          }
1082 1081          /*
1083 1082           * remainder is extension
1084 1083           */
1085 1084          n = PCFEXTSIZE;
1086 1085          if (c == '.') {
1087 1086                  while ((c = *namep++) != '\0' && n--) {
1088 1087                          c = toupper(c);
1089 1088                          if (!pc_validchar(c))
1090 1089                                  return (EINVAL);
1091 1090                          *fextp++ = c;
1092 1091                  }
1093 1092                  if (enable_long_filenames && (c != '\0')) {
1094 1093                          /* not short */
1095 1094                          return (EINVAL);
1096 1095                  }
1097 1096          }
1098 1097          while (n-- > 0) {               /* fill with blanks */
1099 1098                  *fextp++ = ' ';
1100 1099          }
1101 1100          return (0);
1102 1101  }
1103 1102  
1104 1103  /*
1105 1104   * Match a long filename entry with 'namep'. Also return failure
1106 1105   * if the long filename isn't valid.
1107 1106   */
1108 1107  int
1109 1108  pc_match_long_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1110 1109      struct pcslot *slotp, offset_t *offset)
1111 1110  {
1112 1111          struct pcdir *ep = (struct pcdir *)*epp;
1113 1112          struct vnode *vp = PCTOV(pcp);
1114 1113          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1115 1114          int     error = 0;
1116 1115          char    lfn[PCMAXNAMLEN+1];
1117 1116  
1118 1117          error = pc_extract_long_fn(pcp, lfn, epp, offset, &slotp->sl_bp);
1119 1118          if (error) {
1120 1119                  if (error == EINVAL) {
1121 1120                          return (ENOENT);
1122 1121                  } else
1123 1122                          return (error);
1124 1123          }
1125 1124          ep = *epp;
1126 1125          if ((u8_strcmp(lfn, namep, 0, U8_STRCMP_CI_UPPER,
1127 1126              U8_UNICODE_LATEST, &error) == 0) && (error == 0)) {
1128 1127                  /* match */
1129 1128                  slotp->sl_flags = 0;
1130 1129                  slotp->sl_blkno = pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1131 1130                  slotp->sl_offset = pc_blkoff(fsp, *offset);
1132 1131                  slotp->sl_ep = ep;
1133 1132                  return (0);
1134 1133          }
1135 1134          *offset += sizeof (struct pcdir);
1136 1135          ep++;
1137 1136          *epp = ep;
1138 1137          /* If u8_strcmp detected an error it's sufficient to rtn ENOENT */
1139 1138          return (ENOENT);
1140 1139  }
1141 1140  
1142 1141  /*
1143 1142   * Match a short filename entry with namep.
1144 1143   */
1145 1144  int
1146 1145  pc_match_short_fn(struct pcnode *pcp, char *namep, struct pcdir **epp,
1147 1146      struct pcslot *slotp, offset_t *offset)
1148 1147  {
1149 1148          char fname[PCFNAMESIZE];
1150 1149          char fext[PCFEXTSIZE];
1151 1150          struct pcdir *ep = *epp;
1152 1151          int     error;
1153 1152          struct vnode *vp = PCTOV(pcp);
1154 1153          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1155 1154          int boff = pc_blkoff(fsp, *offset);
1156 1155  
1157 1156          if (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) {
1158 1157                  *offset += sizeof (struct pcdir);
1159 1158                  ep++;
1160 1159                  *epp = ep;
1161 1160                  return (ENOENT);
1162 1161          }
1163 1162  
1164 1163          error = pc_parsename(namep, fname, fext);
1165 1164          if (error) {
1166 1165                  *offset += sizeof (struct pcdir);
1167 1166                  ep++;
1168 1167                  *epp = ep;
1169 1168                  return (error);
1170 1169          }
1171 1170  
1172 1171          if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1173 1172              (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1174 1173                  /*
1175 1174                   * found the file
1176 1175                   */
1177 1176                  if (fname[0] == '.') {
1178 1177                          if (fname[1] == '.')
1179 1178                                  slotp->sl_flags = SL_DOTDOT;
1180 1179                          else
1181 1180                                  slotp->sl_flags = SL_DOT;
1182 1181                  } else {
1183 1182                          slotp->sl_flags = 0;
1184 1183                  }
1185 1184                  slotp->sl_blkno =
1186 1185                      pc_daddrdb(fsp, slotp->sl_bp->b_blkno);
1187 1186                  slotp->sl_offset = boff;
1188 1187                  slotp->sl_ep = ep;
1189 1188                  return (0);
1190 1189          }
1191 1190          *offset += sizeof (struct pcdir);
1192 1191          ep++;
1193 1192          *epp = ep;
1194 1193          return (ENOENT);
1195 1194  }
1196 1195  
1197 1196  /*
1198 1197   * Remove a long filename entry starting at lfn_offset. It must be
1199 1198   * a valid entry or we wouldn't have gotten here. Also remove the
1200 1199   * short filename entry.
1201 1200   */
1202 1201  static int
1203 1202  pc_remove_long_fn(struct pcnode *pcp, offset_t lfn_offset)
1204 1203  {
1205 1204          struct vnode *vp = PCTOV(pcp);
1206 1205          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1207 1206          int boff;
1208 1207          struct buf *bp = NULL;
1209 1208          struct pcdir *ep = NULL;
1210 1209          int     error = 0;
1211 1210  
1212 1211          /*
1213 1212           * if we're in here, we know that the lfn is in the proper format
1214 1213           * of <series-of-lfn-entries> followed by <sfn-entry>
1215 1214           */
1216 1215          for (;;) {
1217 1216                  boff = pc_blkoff(fsp, lfn_offset);
1218 1217                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1219 1218                          if (bp != NULL) {
1220 1219                                  bwrite2(bp);
1221 1220                                  error = geterror(bp);
1222 1221                                  brelse(bp);
1223 1222                                  if (error)
1224 1223                                          return (error);
1225 1224                                  bp = NULL;
1226 1225                          }
1227 1226                          error = pc_blkatoff(pcp, lfn_offset, &bp, &ep);
1228 1227                          if (error)
1229 1228                                  return (error);
1230 1229                  }
1231 1230                  if (!PCDL_IS_LFN(ep)) {
1232 1231                          /* done */
1233 1232                          break;
1234 1233                  }
1235 1234                  /* zap it */
1236 1235                  ep->pcd_filename[0] = PCD_ERASED;
1237 1236                  ep->pcd_attr = 0;
1238 1237                  lfn_offset += sizeof (struct pcdir);
1239 1238                  ep++;
1240 1239          }
1241 1240          /* now we're on the short entry */
1242 1241  
1243 1242          ep->pcd_filename[0] = PCD_ERASED;
1244 1243          ep->pcd_attr = 0;
1245 1244  
1246 1245          if (bp != NULL) {
1247 1246                  bwrite2(bp);
1248 1247                  error = geterror(bp);
1249 1248                  brelse(bp);
1250 1249                  if (error)
1251 1250                          return (error);
1252 1251          }
1253 1252          return (0);
1254 1253  }
1255 1254  
1256 1255  /*
1257 1256   * Find (and allocate) space in the directory denoted by
1258 1257   * 'pcp'. for 'ndirentries' pcdir structures.
1259 1258   * Return the offset at which to start, or -1 for failure.
1260 1259   */
1261 1260  static offset_t
1262 1261  pc_find_free_space(struct pcnode *pcp, int ndirentries)
1263 1262  {
1264 1263          offset_t offset = 0;
1265 1264          offset_t spaceneeded = ndirentries * sizeof (struct pcdir);
1266 1265          offset_t spaceoffset;
1267 1266          offset_t spaceavail = 0;
1268 1267          int boff;
1269 1268          struct buf *bp = NULL;
1270 1269          struct vnode *vp = PCTOV(pcp);
1271 1270          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1272 1271          struct pcdir *ep;
1273 1272          int     error;
1274 1273  
1275 1274          spaceoffset = offset;
1276 1275          while (spaceneeded > spaceavail) {
1277 1276                  /*
1278 1277                   * If offset is on a block boundary,
1279 1278                   * read in the next directory block.
1280 1279                   * Release previous if it exists.
1281 1280                   */
1282 1281                  boff = pc_blkoff(fsp, offset);
1283 1282                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1284 1283                          if (bp != NULL) {
1285 1284                                  brelse(bp);
1286 1285                                  bp = NULL;
1287 1286                          }
1288 1287                          error = pc_blkatoff(pcp, offset, &bp, &ep);
1289 1288                          if (error == ENOENT) {
1290 1289                                  daddr_t bn;
1291 1290  
1292 1291                                  /* extend directory */
1293 1292                                  if (!IS_FAT32(fsp) && (vp->v_flag & VROOT))
1294 1293                                          return (-1);
1295 1294                                  while (spaceneeded > spaceavail) {
1296 1295                                          error = pc_balloc(pcp,
1297 1296                                              pc_lblkno(fsp, offset), 1, &bn);
1298 1297                                          if (error)
1299 1298                                                  return (-1);
1300 1299                                          pcp->pc_size += fsp->pcfs_clsize;
1301 1300                                          spaceavail += fsp->pcfs_clsize;
1302 1301                                          offset += fsp->pcfs_clsize;
1303 1302                                  }
1304 1303                                  return (spaceoffset);
1305 1304                          }
1306 1305                          if (error)
1307 1306                                  return (-1);
1308 1307                  }
1309 1308                  if ((ep->pcd_filename[0] == PCD_UNUSED) ||
1310 1309                      (ep->pcd_filename[0] == PCD_ERASED)) {
1311 1310                          offset += sizeof (struct pcdir);
1312 1311                          spaceavail += sizeof (struct pcdir);
1313 1312                          ep++;
1314 1313                          continue;
1315 1314                  }
1316 1315                  offset += sizeof (struct pcdir);
1317 1316                  spaceavail = 0;
1318 1317                  spaceoffset = offset;
1319 1318                  ep++;
1320 1319          }
1321 1320          if (bp != NULL) {
1322 1321                  brelse(bp);
1323 1322          }
1324 1323          return (spaceoffset);
1325 1324  }
1326 1325  
1327 1326  /*
1328 1327   * Return how many long filename entries are needed.
1329 1328   * A maximum of PCLFNCHUNKSIZE characters per entry, plus one for a
1330 1329   * short filename.
1331 1330   */
1332 1331  static int
1333 1332  direntries_needed(struct pcnode *dp, char *namep)
1334 1333  {
1335 1334          struct pcdir ep;
1336 1335          uint16_t *w2_str;
1337 1336          size_t  u8l, u16l;
1338 1337          int ret;
1339 1338  
1340 1339          if (enable_long_filenames == 0) {
1341 1340                  return (1);
1342 1341          }
1343 1342          if (pc_is_short_file_name(namep, 0)) {
1344 1343                  (void) pc_parsename(namep, ep.pcd_filename, ep.pcd_ext);
1345 1344                  if (!shortname_exists(dp, ep.pcd_filename, ep.pcd_ext)) {
1346 1345                          return (1);
1347 1346                  }
1348 1347          }
1349 1348          if (pc_valid_long_fn(namep, 1)) {
1350 1349                  /*
1351 1350                   * convert to UTF-16 or UNICODE for calculating the entries
1352 1351                   * needed. Conversion will consume at the most 512 bytes
1353 1352                   */
1354 1353                  u16l = PCMAXNAMLEN + 1;
1355 1354                  w2_str = (uint16_t *)kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1356 1355                  u8l = strlen(namep);
1357 1356                  ret = uconv_u8tou16((const uchar_t *)namep, &u8l,
1358 1357                      w2_str, &u16l, UCONV_OUT_LITTLE_ENDIAN);
1359 1358                  kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1360 1359                  if (ret == 0) {
1361 1360                          ret = 1 + u16l / PCLFNCHUNKSIZE;
1362 1361                          if (u16l % PCLFNCHUNKSIZE != 0)
1363 1362                                  ret++;
1364 1363                          return (ret);
1365 1364                  }
1366 1365          }
1367 1366          return (-1);
1368 1367  }
1369 1368  
1370 1369  /*
1371 1370   * Allocate and return an array of pcdir structures for the passed-in
1372 1371   * name. ndirentries tells how many are required (including the short
1373 1372   * filename entry). Just allocate and fill them in properly here so they
1374 1373   * can be written out.
1375 1374   */
1376 1375  static struct pcdir *
1377 1376  pc_name_to_pcdir(struct pcnode *dp, char *namep, int ndirentries, int *errret)
1378 1377  {
1379 1378          struct pcdir *bpcdir;
1380 1379          struct pcdir *ep;
1381 1380          struct pcdir_lfn *lep;
1382 1381          int     i;
1383 1382          uchar_t cksum;
1384 1383          int     nchars;
1385 1384          int     error = 0;
1386 1385          char    *nameend;
1387 1386          uint16_t *w2_str;
1388 1387          size_t  u8l, u16l;
1389 1388          int ret;
1390 1389  
1391 1390          bpcdir = kmem_zalloc(ndirentries * sizeof (struct pcdir), KM_SLEEP);
1392 1391          ep = &bpcdir[ndirentries - 1];
1393 1392          if (ndirentries == 1) {
1394 1393                  (void) pc_parsename(namep, ep->pcd_filename, ep->pcd_ext);
1395 1394                  return (bpcdir);
1396 1395          }
1397 1396  
1398 1397          /* Here we need to convert to UTF-16 or UNICODE for writing */
1399 1398  
1400 1399          u16l = PCMAXNAMLEN + 1;
1401 1400          w2_str = (uint16_t *)kmem_zalloc(PCMAXNAM_UTF16, KM_SLEEP);
1402 1401          u8l = strlen(namep);
1403 1402          ret = uconv_u8tou16((const uchar_t *)namep, &u8l, w2_str, &u16l,
1404 1403              UCONV_OUT_LITTLE_ENDIAN);
1405 1404          if (ret != 0) {
1406 1405                  kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1407 1406                  *errret = ret;
1408 1407                  return (NULL);
1409 1408          }
1410 1409          nameend = (char *)(w2_str + u16l);
1411 1410          u16l %= PCLFNCHUNKSIZE;
1412 1411          if (u16l != 0) {
1413 1412                  nchars = u16l + 1;
1414 1413                  nameend += 2;
1415 1414          } else {
1416 1415                  nchars = PCLFNCHUNKSIZE;
1417 1416          }
1418 1417          nchars *= sizeof (uint16_t);
1419 1418  
1420 1419          /* short file name */
1421 1420          error = generate_short_name(dp, namep, ep);
1422 1421          if (error) {
1423 1422                  kmem_free(bpcdir, ndirentries * sizeof (struct pcdir));
1424 1423                  *errret = error;
1425 1424                  return (NULL);
1426 1425          }
1427 1426          cksum = pc_checksum_long_fn(ep->pcd_filename, ep->pcd_ext);
1428 1427          for (i = 0; i < (ndirentries - 1); i++) {
1429 1428                  /* long file name */
1430 1429                  nameend -= nchars;
1431 1430                  lep = (struct pcdir_lfn *)&bpcdir[i];
1432 1431                  set_long_fn_chunk(lep, nameend, nchars);
1433 1432                  lep->pcdl_attr = PCDL_LFN_BITS;
1434 1433                  lep->pcdl_checksum = cksum;
1435 1434                  lep->pcdl_ordinal = (uchar_t)(ndirentries - i - 1);
1436 1435                  nchars = PCLFNCHUNKSIZE * sizeof (uint16_t);
1437 1436          }
1438 1437          kmem_free((caddr_t)w2_str, PCMAXNAM_UTF16);
1439 1438          lep = (struct pcdir_lfn *)&bpcdir[0];
1440 1439          lep->pcdl_ordinal |= 0x40;
1441 1440          return (bpcdir);
1442 1441  }
1443 1442  
1444 1443  static int
1445 1444  generate_short_name(struct pcnode *dp, char *namep, struct pcdir *inep)
1446 1445  {
1447 1446          int     rev;
1448 1447          int     nchars;
1449 1448          int     i, j;
1450 1449          char    *dot = NULL;
1451 1450          char    fname[PCFNAMESIZE+1];
1452 1451          char    fext[PCFEXTSIZE+1];
1453 1452          char    scratch[8];
1454 1453          int     error = 0;
1455 1454          struct  pcslot slot;
1456 1455          char    shortname[20];
1457 1456          int     force_tilde = 0;
1458 1457  
1459 1458          /*
1460 1459           * generate a unique short file name based on the long input name.
1461 1460           *
1462 1461           * Say, for "This is a very long filename.txt" generate
1463 1462           * "THISIS~1.TXT", or "THISIS~2.TXT" if that's already there.
1464 1463           * Skip invalid short name characters in the long name, plus
1465 1464           * a couple NT skips (space and reverse backslash).
1466 1465           *
1467 1466           * Unfortunately, since this name would be hidden by the normal
1468 1467           * lookup routine, we need to look for it ourselves. But luckily
1469 1468           * we don't need to look at the lfn entries themselves.
1470 1469           */
1471 1470          force_tilde = !pc_is_short_file_name(namep, 1);
1472 1471  
1473 1472          /*
1474 1473           * Strip off leading invalid characters.
1475 1474           * We need this because names like '.login' are now ok, but the
1476 1475           * short name needs to be something like LOGIN~1.
1477 1476           */
1478 1477          for (; *namep != '\0'; namep++) {
1479 1478                  if (*namep == ' ')
1480 1479                          continue;
1481 1480                  if (!pc_validchar(*namep) && !pc_validchar(toupper(*namep)))
1482 1481                          continue;
1483 1482                  break;
1484 1483          }
1485 1484          dot = strrchr(namep, '.');
1486 1485          if (dot != NULL) {
1487 1486                  dot++;
1488 1487                  for (j = 0, i = 0; j < PCFEXTSIZE; i++) {
1489 1488                          if (dot[i] == '\0')
1490 1489                                  break;
1491 1490                          /* skip valid, but not generally good characters */
1492 1491                          if (dot[i] == ' ' || dot[i] == '\\')
1493 1492                                  continue;
1494 1493                          if (pc_validchar(dot[i]))
1495 1494                                  fext[j++] = dot[i];
1496 1495                          else if (pc_validchar(toupper(dot[i])))
1497 1496                                  fext[j++] = toupper(dot[i]);
1498 1497                  }
1499 1498                  for (i = j; i < PCFEXTSIZE; i++)
1500 1499                          fext[i] = ' ';
1501 1500                  dot--;
1502 1501          } else {
1503 1502                  for (i = 0; i < PCFEXTSIZE; i++) {
1504 1503                          fext[i] = ' ';
1505 1504                  }
1506 1505          }
1507 1506          /*
1508 1507           * We know we're a long name, not a short name (or we wouldn't
1509 1508           * be here at all. But if uppercasing ourselves would be a short
1510 1509           * name, then we can possibly avoid the ~N format.
1511 1510           */
1512 1511          if (!force_tilde)
1513 1512                  rev = 0;
1514 1513          else
1515 1514                  rev = 1;
1516 1515          for (;;) {
1517 1516                  bzero(fname, sizeof (fname));
1518 1517                  nchars = PCFNAMESIZE;
1519 1518                  if (rev) {
1520 1519                          nchars--; /* ~ */
1521 1520                          i = rev;
1522 1521                          do {
1523 1522                                  nchars--;
1524 1523                                  i /= 10;
1525 1524                          } while (i);
1526 1525                          if (nchars <= 0) {
1527 1526                                  return (ENOSPC);
1528 1527                          }
1529 1528                  }
1530 1529                  for (j = 0, i = 0; j < nchars; i++) {
1531 1530                          if ((&namep[i] == dot) || (namep[i] == '\0'))
1532 1531                                  break;
1533 1532                          /* skip valid, but not generally good characters */
1534 1533                          if (namep[i] == ' ' || namep[i] == '\\')
1535 1534                                  continue;
1536 1535                          if (pc_validchar(namep[i]))
1537 1536                                  fname[j++] = namep[i];
1538 1537                          else if (pc_validchar(toupper(namep[i])))
1539 1538                                  fname[j++] = toupper(namep[i]);
1540 1539                  }
1541 1540                  if (rev) {
1542 1541                          (void) sprintf(scratch, "~%d", rev);
1543 1542                          (void) strcat(fname, scratch);
1544 1543                  }
1545 1544                  for (i = strlen(fname); i < PCFNAMESIZE; i++)
1546 1545                          fname[i] = ' ';
1547 1546                  /* now see if it exists */
1548 1547                  (void) pc_fname_ext_to_name(shortname, fname, fext, 0);
1549 1548                  error = pc_findentry(dp, shortname, &slot, NULL);
1550 1549                  if (error == 0) {
1551 1550                          /* found it */
1552 1551                          brelse(slot.sl_bp);
1553 1552                          rev++;
1554 1553                          continue;
1555 1554                  }
1556 1555                  if (!shortname_exists(dp, fname, fext))
1557 1556                          break;
1558 1557                  rev++;
1559 1558          }
1560 1559          (void) strncpy(inep->pcd_filename, fname, PCFNAMESIZE);
1561 1560          (void) strncpy(inep->pcd_ext, fext, PCFEXTSIZE);
1562 1561          return (0);
1563 1562  }
1564 1563  
1565 1564  /*
1566 1565   * Returns 1 if the passed-in filename is a short name, 0 if not.
1567 1566   */
1568 1567  static int
1569 1568  pc_is_short_file_name(char *namep, int foldcase)
1570 1569  {
1571 1570          int     i;
1572 1571          char    c;
1573 1572  
1574 1573          for (i = 0; i < PCFNAMESIZE; i++, namep++) {
1575 1574                  if (*namep == '\0')
1576 1575                          return (1);
1577 1576                  if (*namep == '.')
1578 1577                          break;
1579 1578                  if (foldcase)
1580 1579                          c = toupper(*namep);
1581 1580                  else
1582 1581                          c = *namep;
1583 1582                  if (!pc_validchar(c))
1584 1583                          return (0);
1585 1584          }
1586 1585          if (*namep == '\0')
1587 1586                  return (1);
1588 1587          if (*namep != '.')
1589 1588                  return (0);
1590 1589          namep++;
1591 1590          for (i = 0; i < PCFEXTSIZE; i++, namep++) {
1592 1591                  if (*namep == '\0')
1593 1592                          return (1);
1594 1593                  if (foldcase)
1595 1594                          c = toupper(*namep);
1596 1595                  else
1597 1596                          c = *namep;
1598 1597                  if (!pc_validchar(c))
1599 1598                          return (0);
1600 1599          }
1601 1600          /* we should be done. If not... */
1602 1601          if (*namep == '\0')
1603 1602                  return (1);
1604 1603          return (0);
1605 1604  
1606 1605  }
1607 1606  
1608 1607  /*
1609 1608   * We call this when we want to see if a short filename already exists
1610 1609   * in the filesystem as part of a long filename. When creating a short
1611 1610   * name (FILENAME.TXT from the user, or when generating one for a long
1612 1611   * filename), we cannot allow one that is part of a long filename.
1613 1612   * pc_findentry will find all the names that are visible (long or short),
1614 1613   * but will not crack any long filename entries.
1615 1614   */
1616 1615  static int
1617 1616  shortname_exists(struct pcnode *dp, char *fname, char *fext)
1618 1617  {
1619 1618          struct buf *bp = NULL;
1620 1619          int     offset = 0;
1621 1620          int     match = 0;
1622 1621          struct pcdir *ep;
1623 1622          struct vnode *vp = PCTOV(dp);
1624 1623          struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp);
1625 1624          int     boff;
1626 1625          int     error = 0;
1627 1626  
1628 1627          for (;;) {
1629 1628                  boff = pc_blkoff(fsp, offset);
1630 1629                  if (boff == 0 || bp == NULL || boff >= bp->b_bcount) {
1631 1630                          if (bp != NULL) {
1632 1631                                  brelse(bp);
1633 1632                                  bp = NULL;
1634 1633                          }
1635 1634                          error = pc_blkatoff(dp, offset, &bp, &ep);
1636 1635                          if (error == ENOENT)
1637 1636                                  break;
1638 1637                          if (error) {
1639 1638                                  return (1);
1640 1639                          }
1641 1640                  }
1642 1641                  if (PCDL_IS_LFN(ep) ||
1643 1642                      (ep->pcd_filename[0] == PCD_ERASED)) {
1644 1643                          offset += sizeof (struct pcdir);
1645 1644                          ep++;
1646 1645                          continue;
1647 1646                  }
1648 1647                  if (ep->pcd_filename[0] == PCD_UNUSED)
1649 1648                          break;
1650 1649                  /*
1651 1650                   * in use, and a short file name (either standalone
1652 1651                   * or associated with a long name
1653 1652                   */
1654 1653                  if ((bcmp(fname, ep->pcd_filename, PCFNAMESIZE) == 0) &&
1655 1654                      (bcmp(fext, ep->pcd_ext, PCFEXTSIZE) == 0)) {
1656 1655                          match = 1;
1657 1656                          break;
1658 1657                  }
1659 1658                  offset += sizeof (struct pcdir);
1660 1659                  ep++;
1661 1660          }
1662 1661          if (bp) {
1663 1662                  brelse(bp);
1664 1663                  bp = NULL;
1665 1664          }
1666 1665          return (match);
1667 1666  }
1668 1667  
1669 1668  pc_cluster32_t
1670 1669  pc_getstartcluster(struct pcfs *fsp, struct pcdir *ep)
1671 1670  {
1672 1671          if (IS_FAT32(fsp)) {
1673 1672                  pc_cluster32_t cn;
1674 1673                  pc_cluster16_t hi16;
1675 1674                  pc_cluster16_t lo16;
1676 1675  
1677 1676                  hi16 = ltohs(ep->un.pcd_scluster_hi);
1678 1677                  lo16 = ltohs(ep->pcd_scluster_lo);
1679 1678                  cn = (hi16 << 16) | lo16;
1680 1679                  return (cn);
1681 1680          } else {
1682 1681                  return (ltohs(ep->pcd_scluster_lo));
1683 1682          }
1684 1683  }
1685 1684  
1686 1685  void
1687 1686  pc_setstartcluster(struct pcfs *fsp, struct pcdir *ep, pc_cluster32_t cln)
1688 1687  {
1689 1688          if (IS_FAT32(fsp)) {
1690 1689                  pc_cluster16_t hi16;
1691 1690                  pc_cluster16_t lo16;
1692 1691  
1693 1692                  hi16 = (cln >> 16) & 0xFFFF;
1694 1693                  lo16 = cln & 0xFFFF;
1695 1694                  ep->un.pcd_scluster_hi = htols(hi16);
1696 1695                  ep->pcd_scluster_lo = htols(lo16);
1697 1696          } else {
1698 1697                  pc_cluster16_t cln16;
1699 1698  
1700 1699                  cln16 = (pc_cluster16_t)cln;
1701 1700                  ep->pcd_scluster_lo = htols(cln16);
1702 1701          }
1703 1702  }
  
    | 
      ↓ open down ↓ | 
    863 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX