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