1 /* $NetBSD: dtfs_subr.c,v 1.3 2011/03/01 15:19:49 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/time.h> 30 31 #include <assert.h> 32 #include <err.h> 33 #include <errno.h> 34 #include <puffs.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <util.h> 39 40 #include "dtfs.h" 41 42 void 43 dtfs_baseattrs(struct vattr *vap, enum vtype type, ino_t id) 44 { 45 struct timeval tv; 46 struct timespec ts; 47 48 gettimeofday(&tv, NULL); 49 TIMEVAL_TO_TIMESPEC(&tv, &ts); 50 51 vap->va_type = type; 52 if (type == VDIR) { 53 vap->va_mode = 0777; 54 vap->va_nlink = 1; /* n + 1 after adding dent */ 55 } else { 56 vap->va_mode = 0666; 57 vap->va_nlink = 0; /* n + 1 */ 58 } 59 vap->va_uid = 0; 60 vap->va_gid = 0; 61 vap->va_fileid = id; 62 vap->va_size = 0; 63 vap->va_blocksize = getpagesize(); 64 vap->va_gen = random(); 65 vap->va_flags = 0; 66 vap->va_rdev = PUFFS_VNOVAL; 67 vap->va_bytes = 0; 68 vap->va_filerev = 1; 69 vap->va_vaflags = 0; 70 71 vap->va_atime = vap->va_mtime = vap->va_ctime = vap->va_birthtime = ts; 72 } 73 74 /* 75 * Well, as you can probably see, this interface has the slight problem 76 * of assuming file creation will always be succesful, or at least not 77 * giving a reason for the failure. Be sure to do better when you 78 * implement your own fs. 79 */ 80 struct puffs_node * 81 dtfs_genfile(struct puffs_node *dir, const struct puffs_cn *pcn, 82 enum vtype type) 83 { 84 struct dtfs_file *df_dir, *dff; 85 struct dtfs_dirent *dfd; 86 struct dtfs_mount *dtm; 87 struct puffs_node *newpn; 88 uid_t uid; 89 int rv; 90 91 assert(dir->pn_va.va_type == VDIR); 92 assert(dir->pn_mnt != NULL); 93 94 uid = 0; 95 rv = puffs_cred_getuid(pcn->pcn_cred, &uid); 96 assert(rv == 0); 97 98 if (type == VDIR) { 99 dff = dtfs_newdir(); 100 dff->df_dotdot = dir; 101 } else 102 dff = dtfs_newfile(); 103 104 dtm = puffs_pn_getmntspecific(dir); 105 newpn = puffs_pn_new(dir->pn_mnt, dff); 106 if (newpn == NULL) 107 errx(1, "getnewpnode"); 108 dtfs_baseattrs(&newpn->pn_va, type, dtm->dtm_nextfileid++); 109 110 df_dir = dir->pn_data; 111 dfd = emalloc(sizeof(struct dtfs_dirent)); 112 dfd->dfd_node = newpn; 113 dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen); 114 dfd->dfd_namelen = strlen(dfd->dfd_name); 115 dfd->dfd_parent = dir; 116 dtfs_adddent(dir, dfd); 117 118 newpn->pn_va.va_uid = uid; 119 newpn->pn_va.va_gid = dir->pn_va.va_gid; 120 121 return newpn; 122 } 123 124 struct dtfs_file * 125 dtfs_newdir() 126 { 127 struct dtfs_file *dff; 128 129 dff = emalloc(sizeof(struct dtfs_file)); 130 memset(dff, 0, sizeof(struct dtfs_file)); 131 LIST_INIT(&dff->df_dirents); 132 133 return dff; 134 } 135 136 struct dtfs_file * 137 dtfs_newfile() 138 { 139 struct dtfs_file *dff; 140 141 dff = emalloc(sizeof(struct dtfs_file)); 142 memset(dff, 0, sizeof(struct dtfs_file)); 143 144 return dff; 145 } 146 147 struct dtfs_dirent * 148 dtfs_dirgetnth(struct dtfs_file *searchdir, int n) 149 { 150 struct dtfs_dirent *dirent; 151 int i; 152 153 i = 0; 154 LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) { 155 if (i == n) 156 return dirent; 157 i++; 158 } 159 160 return NULL; 161 } 162 163 struct dtfs_dirent * 164 dtfs_dirgetbyname(struct dtfs_file *searchdir, const char *fname, size_t fnlen) 165 { 166 struct dtfs_dirent *dirent; 167 168 LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) 169 if (dirent->dfd_namelen == fnlen 170 && strncmp(dirent->dfd_name, fname, fnlen) == 0) 171 return dirent; 172 173 return NULL; 174 } 175 176 /* 177 * common nuke, kill dirent from parent node 178 */ 179 void 180 dtfs_nukenode(struct puffs_node *nukeme, struct puffs_node *pn_parent, 181 const char *fname, size_t fnlen) 182 { 183 struct dtfs_dirent *dfd; 184 struct dtfs_mount *dtm; 185 186 assert(pn_parent->pn_va.va_type == VDIR); 187 188 dfd = dtfs_dirgetbyname(DTFS_PTOF(pn_parent), fname, fnlen); 189 assert(dfd); 190 191 dtm = puffs_pn_getmntspecific(nukeme); 192 dtm->dtm_nfiles--; 193 assert(dtm->dtm_nfiles >= 1); 194 195 dtfs_removedent(pn_parent, dfd); 196 free(dfd); 197 } 198 199 /* free lingering information */ 200 void 201 dtfs_freenode(struct puffs_node *pn) 202 { 203 struct dtfs_file *df = DTFS_PTOF(pn); 204 struct dtfs_mount *dtm; 205 int i; 206 207 assert(pn->pn_va.va_nlink == 0); 208 dtm = puffs_pn_getmntspecific(pn); 209 210 switch (pn->pn_va.va_type) { 211 case VREG: 212 assert(dtm->dtm_fsizes >= pn->pn_va.va_size); 213 dtm->dtm_fsizes -= pn->pn_va.va_size; 214 for (i = 0; i < BLOCKNUM(df->df_datalen, DTFS_BLOCKSHIFT); i++) 215 free(df->df_blocks[i]); 216 if (df->df_datalen > i << DTFS_BLOCKSHIFT) 217 free(df->df_blocks[i]); 218 break; 219 case VLNK: 220 free(df->df_linktarget); 221 break; 222 case VCHR: 223 case VBLK: 224 case VDIR: 225 case VSOCK: 226 case VFIFO: 227 break; 228 default: 229 assert(0); 230 break; 231 } 232 233 free(df); 234 puffs_pn_put(pn); 235 } 236 237 void 238 dtfs_setsize(struct puffs_node *pn, off_t newsize) 239 { 240 struct dtfs_file *df = DTFS_PTOF(pn); 241 struct dtfs_mount *dtm; 242 size_t newblocks; 243 int needalloc, shrinks; 244 int i; 245 246 needalloc = newsize > ROUNDUP(df->df_datalen, DTFS_BLOCKSIZE); 247 shrinks = newsize < pn->pn_va.va_size; 248 249 if (needalloc || shrinks) { 250 newblocks = BLOCKNUM(newsize, DTFS_BLOCKSHIFT) + 1; 251 252 if (shrinks) 253 for (i = newblocks; i < df->df_numblocks; i++) 254 free(df->df_blocks[i]); 255 256 df->df_blocks = erealloc(df->df_blocks, 257 newblocks * sizeof(uint8_t *)); 258 /* 259 * if extended, set storage to zero 260 * to match correct behaviour 261 */ 262 if (!shrinks) { 263 for (i = df->df_numblocks; i < newblocks; i++) { 264 df->df_blocks[i] = emalloc(DTFS_BLOCKSIZE); 265 memset(df->df_blocks[i], 0, DTFS_BLOCKSIZE); 266 } 267 } 268 269 df->df_datalen = newsize; 270 df->df_numblocks = newblocks; 271 } 272 273 dtm = puffs_pn_getmntspecific(pn); 274 if (!shrinks) { 275 dtm->dtm_fsizes += newsize - pn->pn_va.va_size; 276 } else { 277 dtm->dtm_fsizes -= pn->pn_va.va_size - newsize; 278 } 279 280 pn->pn_va.va_size = newsize; 281 pn->pn_va.va_bytes = BLOCKNUM(newsize,DTFS_BLOCKSHIFT)>>DTFS_BLOCKSHIFT; 282 } 283 284 /* add & bump link count */ 285 void 286 dtfs_adddent(struct puffs_node *pn_dir, struct dtfs_dirent *dent) 287 { 288 struct dtfs_file *dir = DTFS_PTOF(pn_dir); 289 struct puffs_node *pn_file = dent->dfd_node; 290 struct dtfs_file *file = DTFS_PTOF(pn_file); 291 struct dtfs_mount *dtm; 292 293 assert(pn_dir->pn_va.va_type == VDIR); 294 LIST_INSERT_HEAD(&dir->df_dirents, dent, dfd_entries); 295 pn_file->pn_va.va_nlink++; 296 297 dtm = puffs_pn_getmntspecific(pn_file); 298 dtm->dtm_nfiles++; 299 300 dent->dfd_parent = pn_dir; 301 if (dent->dfd_node->pn_va.va_type == VDIR) { 302 file->df_dotdot = pn_dir; 303 pn_dir->pn_va.va_nlink++; 304 } 305 306 dtfs_updatetimes(pn_dir, 0, 1, 1); 307 } 308 309 /* remove & lower link count */ 310 void 311 dtfs_removedent(struct puffs_node *pn_dir, struct dtfs_dirent *dent) 312 { 313 struct puffs_node *pn_file = dent->dfd_node; 314 315 assert(pn_dir->pn_va.va_type == VDIR); 316 LIST_REMOVE(dent, dfd_entries); 317 if (pn_file->pn_va.va_type == VDIR) { 318 struct dtfs_file *df = DTFS_PTOF(pn_file); 319 320 pn_dir->pn_va.va_nlink--; 321 df->df_dotdot = NULL; 322 } 323 pn_file->pn_va.va_nlink--; 324 assert(pn_dir->pn_va.va_nlink >= 2); 325 326 dtfs_updatetimes(pn_dir, 0, 1, 1); 327 } 328 329 void 330 dtfs_updatetimes(struct puffs_node *pn, int doatime, int doctime, int domtime) 331 { 332 struct timeval tv; 333 struct timespec ts; 334 335 gettimeofday(&tv, NULL); 336 TIMEVAL_TO_TIMESPEC(&tv, &ts); 337 338 if (doatime) 339 pn->pn_va.va_atime = ts; 340 if (doctime) 341 pn->pn_va.va_ctime = ts; 342 if (domtime) 343 pn->pn_va.va_mtime = ts; 344 } 345 346 bool 347 dtfs_isunder(struct puffs_node *pn, struct puffs_node *pn_parent) 348 { 349 struct dtfs_file *df; 350 351 while (pn) { 352 if (pn == pn_parent) 353 return true; 354 df = DTFS_CTOF(pn); 355 pn = df->df_dotdot; 356 } 357 358 return false; 359 } 360