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