1 1.8 thorpej /* $NetBSD: msdosfs_denode.c,v 1.8 2021/10/23 16:58:17 thorpej Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 5 1.1 christos * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 6 1.1 christos * All rights reserved. 7 1.1 christos * Original code by Paul Popelka (paulp (at) uts.amdahl.com) (see below). 8 1.1 christos * 9 1.1 christos * Redistribution and use in source and binary forms, with or without 10 1.1 christos * modification, are permitted provided that the following conditions 11 1.1 christos * are met: 12 1.1 christos * 1. Redistributions of source code must retain the above copyright 13 1.1 christos * notice, this list of conditions and the following disclaimer. 14 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 christos * notice, this list of conditions and the following disclaimer in the 16 1.1 christos * documentation and/or other materials provided with the distribution. 17 1.1 christos * 3. All advertising materials mentioning features or use of this software 18 1.1 christos * must display the following acknowledgement: 19 1.1 christos * This product includes software developed by TooLs GmbH. 20 1.1 christos * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 1.1 christos * derived from this software without specific prior written permission. 22 1.1 christos * 23 1.1 christos * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 1.1 christos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 1.1 christos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 1.1 christos * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 1.1 christos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 1.1 christos * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 1.1 christos * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 1.1 christos * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 1.1 christos * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 1.1 christos * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 1.1 christos */ 34 1.1 christos /* 35 1.1 christos * Written by Paul Popelka (paulp (at) uts.amdahl.com) 36 1.1 christos * 37 1.1 christos * You can do anything you want with this software, just don't say you wrote 38 1.1 christos * it, and don't remove this notice. 39 1.1 christos * 40 1.1 christos * This software is provided "as is". 41 1.1 christos * 42 1.1 christos * The author supplies this software to be publicly redistributed on the 43 1.1 christos * understanding that the author is not responsible for the correct 44 1.1 christos * functioning of this software in any circumstances and is not liable for 45 1.1 christos * any damages caused by this software. 46 1.1 christos * 47 1.1 christos * October 1992 48 1.1 christos */ 49 1.1 christos 50 1.1 christos #if HAVE_NBTOOL_CONFIG_H 51 1.1 christos #include "nbtool_config.h" 52 1.1 christos #endif 53 1.1 christos 54 1.1 christos #include <sys/cdefs.h> 55 1.8 thorpej __KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.8 2021/10/23 16:58:17 thorpej Exp $"); 56 1.1 christos 57 1.1 christos #include <sys/param.h> 58 1.2 christos 59 1.1 christos #include <ffs/buf.h> 60 1.1 christos 61 1.1 christos #include <fs/msdosfs/bpb.h> 62 1.1 christos #include <fs/msdosfs/msdosfsmount.h> 63 1.1 christos #include <fs/msdosfs/direntry.h> 64 1.1 christos #include <fs/msdosfs/denode.h> 65 1.1 christos #include <fs/msdosfs/fat.h> 66 1.1 christos 67 1.5 christos #include <util.h> 68 1.5 christos 69 1.1 christos /* 70 1.1 christos * If deget() succeeds it returns with the gotten denode locked(). 71 1.1 christos * 72 1.1 christos * pmp - address of msdosfsmount structure of the filesystem containing 73 1.1 christos * the denode of interest. The pm_dev field and the address of 74 1.1 christos * the msdosfsmount structure are used. 75 1.1 christos * dirclust - which cluster bp contains, if dirclust is 0 (root directory) 76 1.1 christos * diroffset is relative to the beginning of the root directory, 77 1.1 christos * otherwise it is cluster relative. 78 1.1 christos * diroffset - offset past begin of cluster of denode we want 79 1.1 christos * depp - returns the address of the gotten denode. 80 1.1 christos */ 81 1.1 christos int 82 1.8 thorpej msdosfs_deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset, 83 1.5 christos struct denode **depp) 84 1.1 christos /* pmp: so we know the maj/min number */ 85 1.1 christos /* dirclust: cluster this dir entry came from */ 86 1.1 christos /* diroffset: index of entry within the cluster */ 87 1.1 christos /* depp: returns the addr of the gotten denode */ 88 1.1 christos { 89 1.1 christos int error; 90 1.1 christos struct direntry *direntptr; 91 1.1 christos struct denode *ldep; 92 1.1 christos struct buf *bp; 93 1.1 christos 94 1.1 christos #ifdef MSDOSFS_DEBUG 95 1.1 christos printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n", 96 1.1 christos pmp, dirclust, diroffset, depp); 97 1.1 christos #endif 98 1.1 christos 99 1.1 christos /* 100 1.1 christos * On FAT32 filesystems, root is a (more or less) normal 101 1.1 christos * directory 102 1.1 christos */ 103 1.1 christos if (FAT32(pmp) && dirclust == MSDOSFSROOT) 104 1.1 christos dirclust = pmp->pm_rootdirblk; 105 1.1 christos 106 1.5 christos ldep = ecalloc(1, sizeof(*ldep)); 107 1.1 christos ldep->de_vnode = NULL; 108 1.1 christos ldep->de_flag = 0; 109 1.1 christos ldep->de_devvp = 0; 110 1.1 christos ldep->de_lockf = 0; 111 1.1 christos ldep->de_dev = pmp->pm_dev; 112 1.1 christos ldep->de_dirclust = dirclust; 113 1.1 christos ldep->de_diroffset = diroffset; 114 1.1 christos ldep->de_pmp = pmp; 115 1.1 christos ldep->de_devvp = pmp->pm_devvp; 116 1.1 christos ldep->de_refcnt = 1; 117 1.8 thorpej msdosfs_fc_purge(ldep, 0); 118 1.1 christos /* 119 1.1 christos * Copy the directory entry into the denode area of the vnode. 120 1.1 christos */ 121 1.1 christos if ((dirclust == MSDOSFSROOT 122 1.1 christos || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) 123 1.1 christos && diroffset == MSDOSFSROOT_OFS) { 124 1.1 christos /* 125 1.1 christos * Directory entry for the root directory. There isn't one, 126 1.1 christos * so we manufacture one. We should probably rummage 127 1.1 christos * through the root directory and find a label entry (if it 128 1.1 christos * exists), and then use the time and date from that entry 129 1.1 christos * as the time and date for the root denode. 130 1.1 christos */ 131 1.1 christos ldep->de_vnode = (struct vnode *)-1; 132 1.1 christos 133 1.1 christos ldep->de_Attributes = ATTR_DIRECTORY; 134 1.1 christos if (FAT32(pmp)) 135 1.1 christos ldep->de_StartCluster = pmp->pm_rootdirblk; 136 1.1 christos /* de_FileSize will be filled in further down */ 137 1.1 christos else { 138 1.1 christos ldep->de_StartCluster = MSDOSFSROOT; 139 1.1 christos ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; 140 1.1 christos } 141 1.1 christos /* 142 1.1 christos * fill in time and date so that dos2unixtime() doesn't 143 1.1 christos * spit up when called from msdosfs_getattr() with root 144 1.1 christos * denode 145 1.1 christos */ 146 1.1 christos ldep->de_CHun = 0; 147 1.1 christos ldep->de_CTime = 0x0000; /* 00:00:00 */ 148 1.1 christos ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) 149 1.1 christos | (1 << DD_DAY_SHIFT); 150 1.1 christos /* Jan 1, 1980 */ 151 1.1 christos ldep->de_ADate = ldep->de_CDate; 152 1.1 christos ldep->de_MTime = ldep->de_CTime; 153 1.1 christos ldep->de_MDate = ldep->de_CDate; 154 1.1 christos /* leave the other fields as garbage */ 155 1.1 christos } else { 156 1.8 thorpej error = msdosfs_readep(pmp, dirclust, diroffset, &bp, 157 1.8 thorpej &direntptr); 158 1.1 christos if (error) { 159 1.1 christos ldep->de_devvp = NULL; 160 1.1 christos ldep->de_Name[0] = SLOT_DELETED; 161 1.1 christos return (error); 162 1.1 christos } 163 1.1 christos DE_INTERNALIZE(ldep, direntptr); 164 1.1 christos brelse(bp, 0); 165 1.1 christos } 166 1.1 christos 167 1.1 christos /* 168 1.1 christos * Fill in a few fields of the vnode and finish filling in the 169 1.1 christos * denode. Then return the address of the found denode. 170 1.1 christos */ 171 1.1 christos if (ldep->de_Attributes & ATTR_DIRECTORY) { 172 1.1 christos /* 173 1.1 christos * Since DOS directory entries that describe directories 174 1.1 christos * have 0 in the filesize field, we take this opportunity 175 1.1 christos * to find out the length of the directory and plug it into 176 1.1 christos * the denode structure. 177 1.1 christos */ 178 1.1 christos u_long size; 179 1.1 christos 180 1.1 christos if (ldep->de_StartCluster != MSDOSFSROOT) { 181 1.8 thorpej error = msdosfs_pcbmap(ldep, CLUST_END, 0, &size, 0); 182 1.1 christos if (error == E2BIG) { 183 1.1 christos ldep->de_FileSize = de_cn2off(pmp, size); 184 1.1 christos error = 0; 185 1.1 christos } else 186 1.1 christos printf("deget(): pcbmap returned %d\n", error); 187 1.1 christos } 188 1.1 christos } 189 1.1 christos *depp = ldep; 190 1.1 christos return (0); 191 1.1 christos } 192 1.1 christos 193 1.1 christos /* 194 1.1 christos * Truncate the file described by dep to the length specified by length. 195 1.1 christos */ 196 1.1 christos int 197 1.8 thorpej msdosfs_detrunc(struct denode *dep, u_long length, int flags, 198 1.8 thorpej struct kauth_cred *cred) 199 1.1 christos { 200 1.1 christos int error; 201 1.1 christos int allerror = 0; 202 1.1 christos u_long eofentry; 203 1.1 christos u_long chaintofree = 0; 204 1.1 christos daddr_t bn, lastblock; 205 1.1 christos int boff; 206 1.1 christos int isadir = dep->de_Attributes & ATTR_DIRECTORY; 207 1.1 christos struct buf *bp; 208 1.1 christos struct msdosfsmount *pmp = dep->de_pmp; 209 1.1 christos 210 1.1 christos #ifdef MSDOSFS_DEBUG 211 1.1 christos printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); 212 1.1 christos #endif 213 1.1 christos 214 1.1 christos /* 215 1.1 christos * Disallow attempts to truncate the root directory since it is of 216 1.1 christos * fixed size. That's just the way dos filesystems are. We use 217 1.1 christos * the VROOT bit in the vnode because checking for the directory 218 1.1 christos * bit and a startcluster of 0 in the denode is not adequate to 219 1.1 christos * recognize the root directory at this point in a file or 220 1.1 christos * directory's life. 221 1.1 christos */ 222 1.3 christos if (dep->de_vnode != NULL && !FAT32(pmp)) { 223 1.1 christos printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", 224 1.1 christos dep->de_dirclust, dep->de_diroffset); 225 1.1 christos return (EINVAL); 226 1.1 christos } 227 1.1 christos 228 1.1 christos if (dep->de_FileSize < length) 229 1.8 thorpej return (msdosfs_deextend(dep, length, cred)); 230 1.1 christos lastblock = de_clcount(pmp, length) - 1; 231 1.1 christos 232 1.1 christos /* 233 1.1 christos * If the desired length is 0 then remember the starting cluster of 234 1.1 christos * the file and set the StartCluster field in the directory entry 235 1.1 christos * to 0. If the desired length is not zero, then get the number of 236 1.1 christos * the last cluster in the shortened file. Then get the number of 237 1.1 christos * the first cluster in the part of the file that is to be freed. 238 1.1 christos * Then set the next cluster pointer in the last cluster of the 239 1.1 christos * file to CLUST_EOFE. 240 1.1 christos */ 241 1.1 christos if (length == 0) { 242 1.1 christos chaintofree = dep->de_StartCluster; 243 1.1 christos dep->de_StartCluster = 0; 244 1.1 christos eofentry = ~0; 245 1.1 christos } else { 246 1.8 thorpej error = msdosfs_pcbmap(dep, lastblock, 0, &eofentry, 0); 247 1.1 christos if (error) { 248 1.1 christos #ifdef MSDOSFS_DEBUG 249 1.1 christos printf("detrunc(): pcbmap fails %d\n", error); 250 1.1 christos #endif 251 1.1 christos return (error); 252 1.1 christos } 253 1.1 christos } 254 1.1 christos 255 1.1 christos /* 256 1.1 christos * If the new length is not a multiple of the cluster size then we 257 1.1 christos * must zero the tail end of the new last cluster in case it 258 1.1 christos * becomes part of the file again because of a seek. 259 1.1 christos */ 260 1.1 christos if ((boff = length & pmp->pm_crbomask) != 0) { 261 1.1 christos if (isadir) { 262 1.1 christos bn = cntobn(pmp, eofentry); 263 1.1 christos error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), 264 1.7 agc pmp->pm_bpcluster, B_MODIFY, &bp); 265 1.1 christos if (error) { 266 1.1 christos #ifdef MSDOSFS_DEBUG 267 1.1 christos printf("detrunc(): bread fails %d\n", error); 268 1.1 christos #endif 269 1.1 christos return (error); 270 1.1 christos } 271 1.1 christos memset((char *)bp->b_data + boff, 0, 272 1.1 christos pmp->pm_bpcluster - boff); 273 1.1 christos if (flags & IO_SYNC) 274 1.1 christos bwrite(bp); 275 1.1 christos else 276 1.1 christos bdwrite(bp); 277 1.1 christos } 278 1.1 christos } 279 1.1 christos 280 1.1 christos /* 281 1.1 christos * Write out the updated directory entry. Even if the update fails 282 1.1 christos * we free the trailing clusters. 283 1.1 christos */ 284 1.1 christos dep->de_FileSize = length; 285 1.1 christos if (!isadir) 286 1.1 christos dep->de_flag |= DE_UPDATE|DE_MODIFIED; 287 1.1 christos #ifdef MSDOSFS_DEBUG 288 1.1 christos printf("detrunc(): allerror %d, eofentry %lu\n", 289 1.1 christos allerror, eofentry); 290 1.1 christos #endif 291 1.1 christos 292 1.1 christos /* 293 1.1 christos * If we need to break the cluster chain for the file then do it 294 1.1 christos * now. 295 1.1 christos */ 296 1.1 christos if (eofentry != (u_long)~0) { 297 1.8 thorpej error = msdosfs_fatentry(FAT_GET_AND_SET, pmp, eofentry, 298 1.1 christos &chaintofree, CLUST_EOFE); 299 1.1 christos if (error) { 300 1.1 christos #ifdef MSDOSFS_DEBUG 301 1.1 christos printf("detrunc(): fatentry errors %d\n", error); 302 1.1 christos #endif 303 1.1 christos return (error); 304 1.1 christos } 305 1.1 christos } 306 1.1 christos 307 1.1 christos /* 308 1.1 christos * Now free the clusters removed from the file because of the 309 1.1 christos * truncation. 310 1.1 christos */ 311 1.1 christos if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask)) 312 1.8 thorpej msdosfs_freeclusterchain(pmp, chaintofree); 313 1.1 christos 314 1.1 christos return (allerror); 315 1.1 christos } 316 1.1 christos 317 1.1 christos /* 318 1.1 christos * Extend the file described by dep to length specified by length. 319 1.1 christos */ 320 1.1 christos int 321 1.8 thorpej msdosfs_deextend(struct denode *dep, u_long length, struct kauth_cred *cred) 322 1.1 christos { 323 1.1 christos struct msdosfsmount *pmp = dep->de_pmp; 324 1.6 christos u_long count; 325 1.1 christos int error; 326 1.1 christos 327 1.1 christos /* 328 1.1 christos * The root of a DOS filesystem cannot be extended. 329 1.1 christos */ 330 1.3 christos if (dep->de_vnode != NULL && !FAT32(pmp)) 331 1.2 christos return EINVAL; 332 1.1 christos 333 1.1 christos /* 334 1.1 christos * Directories cannot be extended. 335 1.1 christos */ 336 1.1 christos if (dep->de_Attributes & ATTR_DIRECTORY) 337 1.2 christos return EISDIR; 338 1.1 christos 339 1.1 christos if (length <= dep->de_FileSize) 340 1.2 christos return E2BIG; 341 1.1 christos 342 1.1 christos /* 343 1.1 christos * Compute the number of clusters to allocate. 344 1.1 christos */ 345 1.1 christos count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); 346 1.1 christos if (count > 0) { 347 1.1 christos if (count > pmp->pm_freeclustercount) 348 1.1 christos return (ENOSPC); 349 1.8 thorpej error = msdosfs_extendfile(dep, count, NULL, NULL, DE_CLEAR); 350 1.1 christos if (error) { 351 1.1 christos /* truncate the added clusters away again */ 352 1.8 thorpej (void) msdosfs_detrunc(dep, dep->de_FileSize, 0, cred); 353 1.1 christos return (error); 354 1.1 christos } 355 1.1 christos } 356 1.1 christos 357 1.1 christos /* 358 1.1 christos * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a 359 1.1 christos * memset(); we set the write size so ubc won't read in file data that 360 1.1 christos * is zero'd later. 361 1.1 christos */ 362 1.1 christos dep->de_FileSize = length; 363 1.1 christos dep->de_flag |= DE_UPDATE|DE_MODIFIED; 364 1.1 christos return 0; 365 1.1 christos } 366