1 /* $NetBSD: msdosfs_vnops.c,v 1.22 2022/04/09 10:05:35 riastradh Exp $ */ 2 3 /*- 4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 6 * All rights reserved. 7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 /* 35 * Written by Paul Popelka (paulp@uts.amdahl.com) 36 * 37 * You can do anything you want with this software, just don't say you wrote 38 * it, and don't remove this notice. 39 * 40 * This software is provided "as is". 41 * 42 * The author supplies this software to be publicly redistributed on the 43 * understanding that the author is not responsible for the correct 44 * functioning of this software in any circumstances and is not liable for 45 * any damages caused by this software. 46 * 47 * October 1992 48 */ 49 #if HAVE_NBTOOL_CONFIG_H 50 #include "nbtool_config.h" 51 #endif 52 53 #include <sys/cdefs.h> 54 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.22 2022/04/09 10:05:35 riastradh Exp $"); 55 56 #include <sys/param.h> 57 #include <sys/mman.h> 58 #include <fcntl.h> 59 #include <unistd.h> 60 61 #include <ffs/buf.h> 62 63 #include <fs/msdosfs/bpb.h> 64 #include <fs/msdosfs/direntry.h> 65 #include <fs/msdosfs/denode.h> 66 #include <fs/msdosfs/msdosfsmount.h> 67 #include <fs/msdosfs/fat.h> 68 69 #include "makefs.h" 70 #include "msdos.h" 71 72 #ifdef MSDOSFS_DEBUG 73 #define DPRINTF(a) printf a 74 #else 75 #define DPRINTF(a) 76 #endif 77 /* 78 * Some general notes: 79 * 80 * In the ufs filesystem the inodes, superblocks, and indirect blocks are 81 * read/written using the vnode for the filesystem. Blocks that represent 82 * the contents of a file are read/written using the vnode for the file 83 * (including directories when they are read/written as files). This 84 * presents problems for the dos filesystem because data that should be in 85 * an inode (if dos had them) resides in the directory itself. Since we 86 * must update directory entries without the benefit of having the vnode 87 * for the directory we must use the vnode for the filesystem. This means 88 * that when a directory is actually read/written (via read, write, or 89 * readdir, or seek) we must use the vnode for the filesystem instead of 90 * the vnode for the directory as would happen in ufs. This is to insure we 91 * retrieve the correct block from the buffer cache since the hash value is 92 * based upon the vnode address and the desired block number. 93 */ 94 95 static int msdosfs_wfile(const char *, struct denode *, fsnode *); 96 97 static void 98 msdosfs_times(struct msdosfsmount *pmp, struct denode *dep, 99 const struct stat *st) 100 { 101 struct timespec at; 102 struct timespec mt; 103 104 if (stampst.st_ino) 105 st = &stampst; 106 107 #ifndef HAVE_NBTOOL_CONFIG_H 108 at = st->st_atimespec; 109 mt = st->st_mtimespec; 110 #else 111 at.tv_sec = st->st_atime; 112 at.tv_nsec = 0; 113 mt.tv_sec = st->st_mtime; 114 mt.tv_nsec = 0; 115 #endif 116 msdosfs_unix2dostime(&at, pmp->pm_gmtoff, &dep->de_ADate, 117 NULL, NULL); 118 msdosfs_unix2dostime(&mt, pmp->pm_gmtoff, &dep->de_MDate, 119 &dep->de_MTime, NULL); 120 } 121 122 /* 123 * When we search a directory the blocks containing directory entries are 124 * read and examined. The directory entries contain information that would 125 * normally be in the inode of a unix filesystem. This means that some of 126 * a directory's contents may also be in memory resident denodes (sort of 127 * an inode). This can cause problems if we are searching while some other 128 * process is modifying a directory. To prevent one process from accessing 129 * incompletely modified directory information we depend upon being the 130 * sole owner of a directory block. bread/brelse provide this service. 131 * This being the case, when a process modifies a directory it must first 132 * acquire the disk block that contains the directory entry to be modified. 133 * Then update the disk block and the denode, and then write the disk block 134 * out to disk. This way disk blocks containing directory entries and in 135 * memory denode's will be in synch. 136 */ 137 static int 138 msdosfs_findslot(struct denode *dp, struct componentname *cnp) 139 { 140 daddr_t bn; 141 int error; 142 int slotcount; 143 int slotoffset = 0; 144 int frcn; 145 u_long cluster; 146 int blkoff; 147 u_int diroff; 148 int blsize; 149 struct msdosfsmount *pmp; 150 struct buf *bp = 0; 151 struct direntry *dep; 152 u_char dosfilename[12]; 153 int wincnt = 1; 154 int chksum = -1, chksum_ok; 155 int olddos = 1; 156 157 pmp = dp->de_pmp; 158 159 switch (msdosfs_unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, 160 cnp->cn_namelen, 0)) { 161 case 0: 162 return (EINVAL); 163 case 1: 164 break; 165 case 2: 166 wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr, 167 cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1; 168 break; 169 case 3: 170 olddos = 0; 171 wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr, 172 cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1; 173 break; 174 } 175 176 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 177 wincnt = 1; 178 179 /* 180 * Suppress search for slots unless creating 181 * file and at end of pathname, in which case 182 * we watch for a place to put the new file in 183 * case it doesn't already exist. 184 */ 185 slotcount = 0; 186 DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename)); 187 /* 188 * Search the directory pointed at by vdp for the name pointed at 189 * by cnp->cn_nameptr. 190 */ 191 /* 192 * The outer loop ranges over the clusters that make up the 193 * directory. Note that the root directory is different from all 194 * other directories. It has a fixed number of blocks that are not 195 * part of the pool of allocatable clusters. So, we treat it a 196 * little differently. The root directory starts at "cluster" 0. 197 */ 198 diroff = 0; 199 for (frcn = 0; diroff < dp->de_FileSize; frcn++) { 200 if ((error = msdosfs_pcbmap(dp, frcn, &bn, &cluster, 201 &blsize)) != 0) { 202 if (error == E2BIG) 203 break; 204 return (error); 205 } 206 error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, 207 0, &bp); 208 if (error) { 209 return (error); 210 } 211 for (blkoff = 0; blkoff < blsize; 212 blkoff += sizeof(struct direntry), 213 diroff += sizeof(struct direntry)) { 214 dep = (struct direntry *)((char *)bp->b_data + blkoff); 215 /* 216 * If the slot is empty and we are still looking 217 * for an empty then remember this one. If the 218 * slot is not empty then check to see if it 219 * matches what we are looking for. If the slot 220 * has never been filled with anything, then the 221 * remainder of the directory has never been used, 222 * so there is no point in searching it. 223 */ 224 if (dep->deName[0] == SLOT_EMPTY || 225 dep->deName[0] == SLOT_DELETED) { 226 /* 227 * Drop memory of previous long matches 228 */ 229 chksum = -1; 230 231 if (slotcount < wincnt) { 232 slotcount++; 233 slotoffset = diroff; 234 } 235 if (dep->deName[0] == SLOT_EMPTY) { 236 brelse(bp, 0); 237 goto notfound; 238 } 239 } else { 240 /* 241 * If there wasn't enough space for our 242 * winentries, forget about the empty space 243 */ 244 if (slotcount < wincnt) 245 slotcount = 0; 246 247 /* 248 * Check for Win95 long filename entry 249 */ 250 if (dep->deAttributes == ATTR_WIN95) { 251 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 252 continue; 253 254 chksum = msdosfs_winChkName((const u_char *)cnp->cn_nameptr, 255 cnp->cn_namelen, 256 (struct winentry *)dep, 257 chksum, 258 pmp->pm_flags & MSDOSFSMNT_UTF8); 259 continue; 260 } 261 262 /* 263 * Ignore volume labels (anywhere, not just 264 * the root directory). 265 */ 266 if (dep->deAttributes & ATTR_VOLUME) { 267 chksum = -1; 268 continue; 269 } 270 271 /* 272 * Check for a checksum or name match 273 */ 274 chksum_ok = 275 (chksum == msdosfs_winChksum(dep->deName)); 276 if (!chksum_ok 277 && (!olddos || memcmp(dosfilename, dep->deName, 11))) { 278 chksum = -1; 279 continue; 280 } 281 DPRINTF(("%s(): match blkoff %d, diroff %d\n", 282 __func__, blkoff, diroff)); 283 /* 284 * Remember where this directory 285 * entry came from for whoever did 286 * this lookup. 287 */ 288 dp->de_crap.mlr_fndoffset = diroff; 289 dp->de_crap.mlr_fndcnt = 0; 290 291 return EEXIST; 292 } 293 } /* for (blkoff = 0; .... */ 294 /* 295 * Release the buffer holding the directory cluster just 296 * searched. 297 */ 298 brelse(bp, 0); 299 } /* for (frcn = 0; ; frcn++) */ 300 301 notfound: 302 /* 303 * We hold no disk buffers at this point. 304 */ 305 306 /* 307 * If we get here we didn't find the entry we were looking for. But 308 * that's ok if we are creating or renaming and are at the end of 309 * the pathname and the directory hasn't been removed. 310 */ 311 DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n", 312 __func__, dp->de_refcnt, slotcount, slotoffset)); 313 /* 314 * Fixup the slot description to point to the place where 315 * we might put the new DOS direntry (putting the Win95 316 * long name entries before that) 317 */ 318 if (!slotcount) { 319 slotcount = 1; 320 slotoffset = diroff; 321 } 322 if (wincnt > slotcount) { 323 slotoffset += sizeof(struct direntry) * (wincnt - slotcount); 324 } 325 326 /* 327 * Return an indication of where the new directory 328 * entry should be put. 329 */ 330 dp->de_crap.mlr_fndoffset = slotoffset; 331 dp->de_crap.mlr_fndcnt = wincnt - 1; 332 333 /* 334 * We return with the directory locked, so that 335 * the parameters we set up above will still be 336 * valid if we actually decide to do a direnter(). 337 * We return ni_vp == NULL to indicate that the entry 338 * does not currently exist; we leave a pointer to 339 * the (locked) directory inode in ndp->ni_dvp. 340 * 341 * NB - if the directory is unlocked, then this 342 * information cannot be used. 343 */ 344 return 0; 345 } 346 347 /* 348 * Create a regular file. On entry the directory to contain the file being 349 * created is locked. We must release before we return. 350 */ 351 struct denode * 352 msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node) 353 { 354 struct componentname cn; 355 struct denode ndirent; 356 struct denode *dep; 357 int error; 358 struct stat *st = &node->inode->st; 359 struct msdosfsmount *pmp = pdep->de_pmp; 360 361 cn.cn_nameptr = node->name; 362 cn.cn_namelen = strlen(node->name); 363 364 DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name, 365 st->st_mode, (size_t)st->st_size)); 366 367 /* 368 * If this is the root directory and there is no space left we 369 * can't do anything. This is because the root directory can not 370 * change size. 371 */ 372 if (pdep->de_StartCluster == MSDOSFSROOT 373 && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) { 374 error = ENOSPC; 375 goto bad; 376 } 377 378 /* 379 * Create a directory entry for the file, then call createde() to 380 * have it installed. NOTE: DOS files are always executable. We 381 * use the absence of the owner write bit to make the file 382 * readonly. 383 */ 384 memset(&ndirent, 0, sizeof(ndirent)); 385 if ((error = msdosfs_uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) 386 goto bad; 387 388 ndirent.de_Attributes = (st->st_mode & S_IWUSR) ? 389 ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; 390 ndirent.de_StartCluster = 0; 391 ndirent.de_FileSize = 0; 392 ndirent.de_dev = pdep->de_dev; 393 ndirent.de_devvp = pdep->de_devvp; 394 ndirent.de_pmp = pdep->de_pmp; 395 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 396 msdosfs_times(pmp, &ndirent, st); 397 if ((error = msdosfs_findslot(pdep, &cn)) != 0) 398 goto bad; 399 if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep, 400 &cn)) != 0) 401 goto bad; 402 if ((error = msdosfs_wfile(path, dep, node)) != 0) 403 goto bad; 404 return dep; 405 406 bad: 407 errno = error; 408 return NULL; 409 } 410 static int 411 msdosfs_updatede(struct denode *dep) 412 { 413 struct buf *bp; 414 struct direntry *dirp; 415 int error; 416 417 dep->de_flag &= ~DE_MODIFIED; 418 error = msdosfs_readde(dep, &bp, &dirp); 419 if (error) 420 return error; 421 DE_EXTERNALIZE(dirp, dep); 422 error = bwrite(bp); 423 return error; 424 } 425 426 /* 427 * Write data to a file or directory. 428 */ 429 static int 430 msdosfs_wfile(const char *path, struct denode *dep, fsnode *node) 431 { 432 int error, fd; 433 size_t osize = dep->de_FileSize; 434 struct stat *st = &node->inode->st; 435 size_t nsize, offs; 436 struct msdosfsmount *pmp = dep->de_pmp; 437 struct buf *bp; 438 char *dat; 439 u_long cn = 0; 440 441 error = 0; /* XXX: gcc/vax */ 442 DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__, 443 dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster)); 444 if (st->st_size == 0) 445 return 0; 446 447 /* Don't bother to try to write files larger than the fs limit */ 448 if (st->st_size > MSDOSFS_FILESIZE_MAX) 449 return EFBIG; 450 451 nsize = st->st_size; 452 DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize)); 453 if (nsize > osize) { 454 if ((error = msdosfs_deextend(dep, nsize, NULL)) != 0) 455 return error; 456 if ((error = msdosfs_updatede(dep)) != 0) 457 return error; 458 } 459 460 if ((fd = open(path, O_RDONLY)) == -1) { 461 error = errno; 462 DPRINTF((1, "open %s: %s", path, strerror(error))); 463 return error; 464 } 465 466 if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) 467 == MAP_FAILED) { 468 error = errno; 469 DPRINTF(("%s: mmap %s: %s", __func__, node->name, 470 strerror(error))); 471 close(fd); 472 goto out; 473 } 474 close(fd); 475 476 for (offs = 0; offs < nsize;) { 477 int blsize, cpsize; 478 daddr_t bn; 479 u_long on = offs & pmp->pm_crbomask; 480 #ifdef HACK 481 cn = dep->de_StartCluster; 482 if (cn == MSDOSFSROOT) { 483 DPRINTF(("%s: bad lbn %lu", __func__, cn)); 484 error = EINVAL; 485 goto out; 486 } 487 bn = cntobn(pmp, cn); 488 blsize = pmp->pm_bpcluster; 489 #else 490 if ((error = msdosfs_pcbmap(dep, cn++, &bn, NULL, 491 &blsize)) != 0) { 492 DPRINTF(("%s: pcbmap %lu", __func__, bn)); 493 goto out; 494 } 495 #endif 496 DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__, 497 cn, (unsigned long long)bn, 498 (unsigned long long)de_bn2kb(pmp, bn), blsize)); 499 if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, 500 0, &bp)) != 0) { 501 DPRINTF(("bread %d\n", error)); 502 goto out; 503 } 504 cpsize = MIN((nsize - offs), blsize - on); 505 memcpy((char *)bp->b_data + on, dat + offs, cpsize); 506 bwrite(bp); 507 offs += cpsize; 508 } 509 510 munmap(dat, nsize); 511 return 0; 512 out: 513 munmap(dat, nsize); 514 return error; 515 } 516 517 518 static const struct { 519 struct direntry dot; 520 struct direntry dotdot; 521 } dosdirtemplate = { 522 { ". ", " ", /* the . entry */ 523 ATTR_DIRECTORY, /* file attribute */ 524 0, /* reserved */ 525 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 526 { 0, 0 }, /* access date */ 527 { 0, 0 }, /* high bits of start cluster */ 528 { 210, 4 }, { 210, 4 }, /* modify time & date */ 529 { 0, 0 }, /* startcluster */ 530 { 0, 0, 0, 0 } /* filesize */ 531 }, 532 { ".. ", " ", /* the .. entry */ 533 ATTR_DIRECTORY, /* file attribute */ 534 0, /* reserved */ 535 0, { 0, 0 }, { 0, 0 }, /* create time & date */ 536 { 0, 0 }, /* access date */ 537 { 0, 0 }, /* high bits of start cluster */ 538 { 210, 4 }, { 210, 4 }, /* modify time & date */ 539 { 0, 0 }, /* startcluster */ 540 { 0, 0, 0, 0 } /* filesize */ 541 } 542 }; 543 544 struct denode * 545 msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) { 546 struct denode ndirent; 547 struct denode *dep; 548 struct componentname cn; 549 struct stat *st = &node->inode->st; 550 struct msdosfsmount *pmp = pdep->de_pmp; 551 int error; 552 u_long newcluster, pcl, bn; 553 daddr_t lbn; 554 struct direntry *denp; 555 struct buf *bp; 556 557 cn.cn_nameptr = node->name; 558 cn.cn_namelen = strlen(node->name); 559 /* 560 * If this is the root directory and there is no space left we 561 * can't do anything. This is because the root directory can not 562 * change size. 563 */ 564 if (pdep->de_StartCluster == MSDOSFSROOT 565 && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) { 566 error = ENOSPC; 567 goto bad2; 568 } 569 570 /* 571 * Allocate a cluster to hold the about to be created directory. 572 */ 573 error = msdosfs_clusteralloc(pmp, 0, 1, &newcluster, NULL); 574 if (error) 575 goto bad2; 576 577 memset(&ndirent, 0, sizeof(ndirent)); 578 ndirent.de_pmp = pmp; 579 ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 580 msdosfs_times(pmp, &ndirent, st); 581 582 /* 583 * Now fill the cluster with the "." and ".." entries. And write 584 * the cluster to disk. This way it is there for the parent 585 * directory to be pointing at if there were a crash. 586 */ 587 bn = cntobn(pmp, newcluster); 588 lbn = de_bn2kb(pmp, bn); 589 DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster, 590 bn, lbn)); 591 /* always succeeds */ 592 bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0); 593 memset(bp->b_data, 0, pmp->pm_bpcluster); 594 memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate); 595 denp = (struct direntry *)bp->b_data; 596 putushort(denp[0].deStartCluster, newcluster); 597 putushort(denp[0].deCDate, ndirent.de_CDate); 598 putushort(denp[0].deCTime, ndirent.de_CTime); 599 denp[0].deCHundredth = ndirent.de_CHun; 600 putushort(denp[0].deADate, ndirent.de_ADate); 601 putushort(denp[0].deMDate, ndirent.de_MDate); 602 putushort(denp[0].deMTime, ndirent.de_MTime); 603 pcl = pdep->de_StartCluster; 604 DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl, 605 pmp->pm_rootdirblk)); 606 if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) 607 pcl = 0; 608 putushort(denp[1].deStartCluster, pcl); 609 putushort(denp[1].deCDate, ndirent.de_CDate); 610 putushort(denp[1].deCTime, ndirent.de_CTime); 611 denp[1].deCHundredth = ndirent.de_CHun; 612 putushort(denp[1].deADate, ndirent.de_ADate); 613 putushort(denp[1].deMDate, ndirent.de_MDate); 614 putushort(denp[1].deMTime, ndirent.de_MTime); 615 if (FAT32(pmp)) { 616 putushort(denp[0].deHighClust, newcluster >> 16); 617 putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); 618 } else { 619 putushort(denp[0].deHighClust, 0); 620 putushort(denp[1].deHighClust, 0); 621 } 622 623 if ((error = bwrite(bp)) != 0) 624 goto bad; 625 626 /* 627 * Now build up a directory entry pointing to the newly allocated 628 * cluster. This will be written to an empty slot in the parent 629 * directory. 630 */ 631 if ((error = msdosfs_uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) 632 goto bad; 633 634 ndirent.de_Attributes = ATTR_DIRECTORY; 635 ndirent.de_StartCluster = newcluster; 636 ndirent.de_FileSize = 0; 637 ndirent.de_dev = pdep->de_dev; 638 ndirent.de_devvp = pdep->de_devvp; 639 ndirent.de_pmp = pdep->de_pmp; 640 if ((error = msdosfs_findslot(pdep, &cn)) != 0) 641 goto bad; 642 if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep, 643 &cn)) != 0) 644 goto bad; 645 if ((error = msdosfs_updatede(dep)) != 0) 646 goto bad; 647 return dep; 648 649 bad: 650 msdosfs_clusterfree(pmp, newcluster, NULL); 651 bad2: 652 errno = error; 653 return NULL; 654 } 655