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