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