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