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