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