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