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