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