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