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