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