1 /*	$NetBSD: chfs_write.c,v 1.5 2012/10/19 12:44:39 ttoth Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010 Department of Software Engineering,
5  *		      University of Szeged, Hungary
6  * Copyright (C) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
7  * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
8  * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
9  * All rights reserved.
10  *
11  * This code is derived from software contributed to The NetBSD Foundation
12  * by the Department of Software Engineering, University of Szeged, Hungary
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 
37 #include <sys/param.h>
38 #include <sys/buf.h>
39 
40 #include "chfs.h"
41 
42 
43 /* chfs_write_flash_vnode - writes out a vnode information to flash */
44 int
chfs_write_flash_vnode(struct chfs_mount * chmp,struct chfs_inode * ip,int prio)45 chfs_write_flash_vnode(struct chfs_mount *chmp,
46     struct chfs_inode *ip, int prio)
47 {
48 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
49 
50 	struct chfs_flash_vnode *fvnode;
51 	struct chfs_vnode_cache* chvc;
52 	struct chfs_node_ref *nref;
53 	struct iovec vec;
54 	size_t size, retlen;
55 	int err = 0, retries = 0;
56 
57 	/* root vnode is in-memory only */
58 	if (ip->ino == CHFS_ROOTINO)
59 		return 0;
60 
61 	fvnode = chfs_alloc_flash_vnode();
62 	if (!fvnode)
63 		return ENOMEM;
64 
65 	chvc = ip->chvc;
66 
67 	/* setting up flash_vnode's fields */
68 	size = sizeof(*fvnode);
69 	fvnode->magic = htole16(CHFS_FS_MAGIC_BITMASK);
70 	fvnode->type = htole16(CHFS_NODETYPE_VNODE);
71 	fvnode->length = htole32(CHFS_PAD(size));
72 	fvnode->hdr_crc = htole32(crc32(0, (uint8_t *)fvnode,
73 		CHFS_NODE_HDR_SIZE - 4));
74 	fvnode->vno = htole64(ip->ino);
75 	fvnode->version = htole64(++ip->chvc->highest_version);
76 	fvnode->mode = htole32(ip->mode);
77 	fvnode->dn_size = htole32(ip->size);
78 	fvnode->atime = htole32(ip->atime);
79 	fvnode->ctime = htole32(ip->ctime);
80 	fvnode->mtime = htole32(ip->mtime);
81 	fvnode->gid = htole32(ip->gid);
82 	fvnode->uid = htole32(ip->uid);
83 	fvnode->node_crc = htole32(crc32(0, (uint8_t *)fvnode, size - 4));
84 
85 retry:
86 	/* setting up the next eraseblock where we will write */
87 	if (prio == ALLOC_GC) {
88 		/* GC called this function */
89 		err = chfs_reserve_space_gc(chmp, CHFS_PAD(size));
90 		if (err)
91 			goto out;
92 	} else {
93 		chfs_gc_trigger(chmp);
94 		if (prio == ALLOC_NORMAL)
95 			err = chfs_reserve_space_normal(chmp,
96 			    CHFS_PAD(size), ALLOC_NORMAL);
97 		else
98 			err = chfs_reserve_space_normal(chmp,
99 			    CHFS_PAD(size), ALLOC_DELETION);
100 		if (err)
101 			goto out;
102 	}
103 
104 	/* allocating a new node reference */
105 	nref = chfs_alloc_node_ref(chmp->chm_nextblock);
106 	if (!nref) {
107 		err = ENOMEM;
108 		goto out;
109 	}
110 
111 	mutex_enter(&chmp->chm_lock_sizes);
112 
113 	/* caculating offset and sizes  */
114 	nref->nref_offset = chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size;
115 	chfs_change_size_free(chmp, chmp->chm_nextblock, -CHFS_PAD(size));
116 	vec.iov_base = fvnode;
117 	vec.iov_len = CHFS_PAD(size);
118 
119 	/* write it into the writebuffer */
120 	err = chfs_write_wbuf(chmp, &vec, 1, nref->nref_offset, &retlen);
121 	if (err || retlen != CHFS_PAD(size)) {
122 		/* there was an error during write */
123 		chfs_err("error while writing out flash vnode to the media\n");
124 		chfs_err("err: %d | size: %zu | retlen : %zu\n",
125 		    err, CHFS_PAD(size), retlen);
126 		chfs_change_size_dirty(chmp,
127 		    chmp->chm_nextblock, CHFS_PAD(size));
128 		if (retries) {
129 			err = EIO;
130 			mutex_exit(&chmp->chm_lock_sizes);
131 			goto out;
132 		}
133 
134 		/* try again */
135 		retries++;
136 		mutex_exit(&chmp->chm_lock_sizes);
137 		goto retry;
138 	}
139 
140 	/* everything went well */
141 	chfs_change_size_used(chmp,
142 	    &chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size));
143 	mutex_exit(&chmp->chm_lock_sizes);
144 
145 	/* add the new nref to vnode cache */
146 	mutex_enter(&chmp->chm_lock_vnocache);
147 	chfs_add_vnode_ref_to_vc(chmp, chvc, nref);
148 	mutex_exit(&chmp->chm_lock_vnocache);
149 	KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size);
150 out:
151 	chfs_free_flash_vnode(fvnode);
152 	return err;
153 }
154 
155 /* chfs_write_flash_dirent - writes out a directory entry to flash */
156 int
chfs_write_flash_dirent(struct chfs_mount * chmp,struct chfs_inode * pdir,struct chfs_inode * ip,struct chfs_dirent * fd,ino_t ino,int prio)157 chfs_write_flash_dirent(struct chfs_mount *chmp, struct chfs_inode *pdir,
158     struct chfs_inode *ip, struct chfs_dirent *fd,
159     ino_t ino, int prio)
160 {
161 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
162 
163 	struct chfs_flash_dirent_node *fdirent;
164 	struct chfs_node_ref *nref;
165 	struct iovec vec[2];
166 	size_t size, retlen;
167 	int err = 0, retries = 0;
168 	uint8_t *name;
169 	size_t namelen;
170 
171 	KASSERT(fd->vno != CHFS_ROOTINO);
172 
173 	/* setting up flash_dirent's fields */
174 	fdirent = chfs_alloc_flash_dirent();
175 	if (!fdirent)
176 		return ENOMEM;
177 
178 	size = sizeof(*fdirent) + fd->nsize;
179 	namelen = CHFS_PAD(size) - sizeof(*fdirent);
180 
181 	name = kmem_zalloc(namelen, KM_SLEEP);
182 	memcpy(name, fd->name, fd->nsize);
183 
184 	fdirent->magic = htole16(CHFS_FS_MAGIC_BITMASK);
185 	fdirent->type = htole16(CHFS_NODETYPE_DIRENT);
186 	fdirent->length = htole32(CHFS_PAD(size));
187 	fdirent->hdr_crc = htole32(crc32(0, (uint8_t *)fdirent,
188 		CHFS_NODE_HDR_SIZE - 4));
189 	fdirent->vno = htole64(ino);
190 	fdirent->pvno = htole64(pdir->ino);
191 	fdirent->version = htole64(++pdir->chvc->highest_version);
192 	fdirent->mctime = ip?ip->ctime:0;
193 	fdirent->nsize = fd->nsize;
194 	fdirent->dtype = fd->type;
195 	fdirent->name_crc = crc32(0, (uint8_t *)&(fd->name), fd->nsize);
196 	fdirent->node_crc = crc32(0, (uint8_t *)fdirent, sizeof(*fdirent) - 4);
197 
198 	/* directory's name is written out right after the dirent */
199 	vec[0].iov_base = fdirent;
200 	vec[0].iov_len  = sizeof(*fdirent);
201 	vec[1].iov_base = name;
202 	vec[1].iov_len  = namelen;
203 
204 retry:
205 	/* setting up the next eraseblock where we will write */
206 	if (prio == ALLOC_GC) {
207 		/* the GC calls this function */
208 		err = chfs_reserve_space_gc(chmp, CHFS_PAD(size));
209 		if (err)
210 			goto out;
211 	} else {
212 		chfs_gc_trigger(chmp);
213 		if (prio == ALLOC_NORMAL)
214 			err = chfs_reserve_space_normal(chmp,
215 			    CHFS_PAD(size), ALLOC_NORMAL);
216 		else
217 			err = chfs_reserve_space_normal(chmp,
218 			    CHFS_PAD(size), ALLOC_DELETION);
219 		if (err)
220 			goto out;
221 	}
222 
223 	/* allocating a new node reference */
224 	nref = chfs_alloc_node_ref(chmp->chm_nextblock);
225 	if (!nref) {
226 		err = ENOMEM;
227 		goto out;
228 	}
229 
230 	mutex_enter(&chmp->chm_lock_sizes);
231 
232 	nref->nref_offset = chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size;
233 	chfs_change_size_free(chmp, chmp->chm_nextblock, -CHFS_PAD(size));
234 
235 	/* write it into the writebuffer */
236 	err = chfs_write_wbuf(chmp, vec, 2, nref->nref_offset, &retlen);
237 	if (err || retlen != CHFS_PAD(size)) {
238 		/* there was an error during write */
239 		chfs_err("error while writing out flash dirent node to the media\n");
240 		chfs_err("err: %d | size: %zu | retlen : %zu\n",
241 		    err, CHFS_PAD(size), retlen);
242 		chfs_change_size_dirty(chmp,
243 		    chmp->chm_nextblock, CHFS_PAD(size));
244 		if (retries) {
245 			err = EIO;
246 			mutex_exit(&chmp->chm_lock_sizes);
247 			goto out;
248 		}
249 
250 		/* try again */
251 		retries++;
252 		mutex_exit(&chmp->chm_lock_sizes);
253 		goto retry;
254 	}
255 
256 
257 	/* everything went well */
258 	chfs_change_size_used(chmp,
259 	    &chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size));
260 	mutex_exit(&chmp->chm_lock_sizes);
261 	KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size);
262 
263 	/* add the new nref to the directory chain of vnode cache */
264 	fd->nref = nref;
265 	if (prio != ALLOC_DELETION) {
266 		mutex_enter(&chmp->chm_lock_vnocache);
267 		chfs_add_node_to_list(chmp,
268 			pdir->chvc, nref, &pdir->chvc->dirents);
269 		mutex_exit(&chmp->chm_lock_vnocache);
270 	}
271 out:
272 	chfs_free_flash_dirent(fdirent);
273 	return err;
274 }
275 
276 /* chfs_write_flash_dnode - writes out a data node to flash */
277 int
chfs_write_flash_dnode(struct chfs_mount * chmp,struct vnode * vp,struct buf * bp,struct chfs_full_dnode * fd)278 chfs_write_flash_dnode(struct chfs_mount *chmp, struct vnode *vp,
279     struct buf *bp, struct chfs_full_dnode *fd)
280 {
281 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
282 
283 	int err = 0, retries = 0;
284 	size_t size, retlen;
285 	off_t ofs;
286 	struct chfs_flash_data_node *dnode;
287 	struct chfs_node_ref *nref;
288 	struct chfs_inode *ip = VTOI(vp);
289 	struct iovec vec[2];
290 	uint32_t len;
291 	void *tmpbuf = NULL;
292 
293 	KASSERT(ip->ino != CHFS_ROOTINO);
294 
295 	dnode = chfs_alloc_flash_dnode();
296 	if (!dnode)
297 		return ENOMEM;
298 
299 	/* initialize flash data node */
300 	ofs = bp->b_blkno * PAGE_SIZE;
301 	len = MIN((vp->v_size - ofs), bp->b_resid);
302 	size = sizeof(*dnode) + len;
303 
304 	dnode->magic = htole16(CHFS_FS_MAGIC_BITMASK);
305 	dnode->type = htole16(CHFS_NODETYPE_DATA);
306 	dnode->length = htole32(CHFS_PAD(size));
307 	dnode->hdr_crc = htole32(crc32(0, (uint8_t *)dnode,
308 		CHFS_NODE_HDR_SIZE - 4));
309 	dnode->vno = htole64(ip->ino);
310 	dnode->version = htole64(++ip->chvc->highest_version);
311 	dnode->offset = htole64(ofs);
312 	dnode->data_length = htole32(len);
313 	dnode->data_crc = htole32(crc32(0, (uint8_t *)bp->b_data, len));
314 	dnode->node_crc = htole32(crc32(0, (uint8_t *)dnode,
315 		sizeof(*dnode) - 4));
316 
317 	dbg("dnode @%llu %ub v%llu\n", (unsigned long long)dnode->offset,
318 		dnode->data_length, (unsigned long long)dnode->version);
319 
320 	/* pad data if needed */
321 	if (CHFS_PAD(size) - sizeof(*dnode)) {
322 		tmpbuf = kmem_zalloc(CHFS_PAD(size)
323 		    - sizeof(*dnode), KM_SLEEP);
324 		memcpy(tmpbuf, bp->b_data, len);
325 	}
326 
327 	/* creating iovecs for writebuffer
328 	 * data is written out right after the data node */
329 	vec[0].iov_base = dnode;
330 	vec[0].iov_len = sizeof(*dnode);
331 	vec[1].iov_base = tmpbuf;
332 	vec[1].iov_len = CHFS_PAD(size) - sizeof(*dnode);
333 
334 	fd->ofs = ofs;
335 	fd->size = len;
336 
337 retry:
338 	/* Reserve space for data node. This will set up the next eraseblock
339 	 * where to we will write.
340 	 */
341 	chfs_gc_trigger(chmp);
342 	err = chfs_reserve_space_normal(chmp,
343 	    CHFS_PAD(size), ALLOC_NORMAL);
344 	if (err)
345 		goto out;
346 
347 	/* allocating a new node reference */
348 	nref = chfs_alloc_node_ref(chmp->chm_nextblock);
349 	if (!nref) {
350 		err = ENOMEM;
351 		goto out;
352 	}
353 
354 	nref->nref_offset =
355 	    chmp->chm_ebh->eb_size - chmp->chm_nextblock->free_size;
356 
357 	KASSERT(nref->nref_offset < chmp->chm_ebh->eb_size);
358 
359 	mutex_enter(&chmp->chm_lock_sizes);
360 
361 	chfs_change_size_free(chmp,
362 	    chmp->chm_nextblock, -CHFS_PAD(size));
363 
364 	/* write it into the writebuffer */
365 	err = chfs_write_wbuf(chmp, vec, 2, nref->nref_offset, &retlen);
366 	if (err || retlen != CHFS_PAD(size)) {
367 		/* there was an error during write */
368 		chfs_err("error while writing out flash data node to the media\n");
369 		chfs_err("err: %d | size: %zu | retlen : %zu\n",
370 		    err, size, retlen);
371 		chfs_change_size_dirty(chmp,
372 		    chmp->chm_nextblock, CHFS_PAD(size));
373 		if (retries) {
374 			err = EIO;
375 			mutex_exit(&chmp->chm_lock_sizes);
376 			goto out;
377 		}
378 
379 		/* try again */
380 		retries++;
381 		mutex_exit(&chmp->chm_lock_sizes);
382 		goto retry;
383 	}
384 	/* everything went well */
385 	ip->write_size += fd->size;
386 	chfs_change_size_used(chmp,
387 	    &chmp->chm_blocks[nref->nref_lnr], CHFS_PAD(size));
388 	mutex_exit(&chmp->chm_lock_sizes);
389 
390 	mutex_enter(&chmp->chm_lock_vnocache);
391 	if (fd->nref != NULL) {
392 		chfs_remove_frags_of_node(chmp, &ip->fragtree, fd->nref);
393 		chfs_remove_and_obsolete(chmp, ip->chvc, fd->nref, &ip->chvc->dnode);
394 	}
395 
396 	/* add the new nref to the data node chain of vnode cache */
397 	KASSERT(chmp->chm_blocks[nref->nref_lnr].used_size <= chmp->chm_ebh->eb_size);
398 	fd->nref = nref;
399 	chfs_add_node_to_list(chmp, ip->chvc, nref, &ip->chvc->dnode);
400 	mutex_exit(&chmp->chm_lock_vnocache);
401 out:
402 	chfs_free_flash_dnode(dnode);
403 	if (CHFS_PAD(size) - sizeof(*dnode)) {
404 		kmem_free(tmpbuf, CHFS_PAD(size) - sizeof(*dnode));
405 	}
406 
407 	return err;
408 }
409 
410 /*
411  * chfs_do_link - makes a copy from a node
412  * This function writes the dirent of the new node to the media.
413  */
414 int
chfs_do_link(struct chfs_inode * ip,struct chfs_inode * parent,const char * name,int namelen,enum chtype type)415 chfs_do_link(struct chfs_inode *ip, struct chfs_inode *parent, const char *name, int namelen, enum chtype type)
416 {
417 	int error = 0;
418 	struct vnode *vp = ITOV(ip);
419 	struct ufsmount *ump = VFSTOUFS(vp->v_mount);
420 	struct chfs_mount *chmp = ump->um_chfs;
421 	struct chfs_dirent *newfd = NULL;
422 
423 	/* setting up the new directory entry */
424 	newfd = chfs_alloc_dirent(namelen + 1);
425 
426 	newfd->vno = ip->ino;
427 	newfd->type = type;
428 	newfd->nsize = namelen;
429 	memcpy(newfd->name, name, namelen);
430 	newfd->name[newfd->nsize] = 0;
431 
432 	ip->chvc->nlink++;
433 	parent->chvc->nlink++;
434 	ip->iflag |= IN_CHANGE;
435 	chfs_update(vp, NULL, NULL, UPDATE_WAIT);
436 
437 	mutex_enter(&chmp->chm_lock_mountfields);
438 
439 	/* update vnode information */
440 	error = chfs_write_flash_vnode(chmp, ip, ALLOC_NORMAL);
441 	if (error)
442 		return error;
443 
444 	/* write out the new dirent */
445 	error = chfs_write_flash_dirent(chmp,
446 	    parent, ip, newfd, ip->ino, ALLOC_NORMAL);
447 	/* TODO: what should we do if error isn't zero? */
448 
449 	mutex_exit(&chmp->chm_lock_mountfields);
450 
451 	/* add fd to the fd list */
452 	TAILQ_INSERT_TAIL(&parent->dents, newfd, fds);
453 
454 	return error;
455 }
456 
457 
458 /*
459  * chfs_do_unlink - delete a node
460  * This function set the nlink and vno of the node to zero and
461  * write its dirent to the media.
462  */
463 int
chfs_do_unlink(struct chfs_inode * ip,struct chfs_inode * parent,const char * name,int namelen)464 chfs_do_unlink(struct chfs_inode *ip,
465     struct chfs_inode *parent, const char *name, int namelen)
466 {
467 	struct chfs_dirent *fd, *tmpfd;
468 	int error = 0;
469 	struct vnode *vp = ITOV(ip);
470 	struct ufsmount *ump = VFSTOUFS(vp->v_mount);
471 	struct chfs_mount *chmp = ump->um_chfs;
472 	struct chfs_node_ref *nref;
473 
474 	vflushbuf(vp, 0);
475 
476 	mutex_enter(&chmp->chm_lock_mountfields);
477 
478 	/* remove the full direntry from the parent dents list */
479 	TAILQ_FOREACH_SAFE(fd, &parent->dents, fds, tmpfd) {
480 		if (fd->vno == ip->ino &&
481 		    fd->nsize == namelen &&
482 		    !memcmp(fd->name, name, fd->nsize)) {
483 
484 			/* remove every fragment of the file */
485 			chfs_kill_fragtree(chmp, &ip->fragtree);
486 
487 			/* decrease number of links to the file */
488 			if (fd->type == CHT_DIR && ip->chvc->nlink == 2)
489 				ip->chvc->nlink = 0;
490 			else
491 				ip->chvc->nlink--;
492 
493 			fd->type = CHT_BLANK;
494 
495 			/* remove from parent's directory entries */
496 			TAILQ_REMOVE(&parent->dents, fd, fds);
497 
498 			mutex_enter(&chmp->chm_lock_vnocache);
499 
500 			dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n",
501 			    fd->vno, fd->nref->nref_lnr, fd->nref->nref_offset);
502 			chfs_remove_and_obsolete(chmp, parent->chvc, fd->nref,
503 				&parent->chvc->dirents);
504 
505 			error = chfs_write_flash_dirent(chmp,
506 			    parent, ip, fd, 0, ALLOC_DELETION);
507 
508 			dbg("FD->NREF vno: %llu, lnr: %u, ofs: %u\n",
509 			    fd->vno, fd->nref->nref_lnr, fd->nref->nref_offset);
510 			/* set nref_next field */
511 			chfs_add_node_to_list(chmp, parent->chvc, fd->nref,
512 				&parent->chvc->dirents);
513 			/* remove from the list */
514 			chfs_remove_and_obsolete(chmp, parent->chvc, fd->nref,
515 				&parent->chvc->dirents);
516 
517 			/* clean dnode list */
518 			while (ip->chvc->dnode != (struct chfs_node_ref *)ip->chvc) {
519 				nref = ip->chvc->dnode;
520 				chfs_remove_frags_of_node(chmp, &ip->fragtree, nref);
521 				chfs_remove_and_obsolete(chmp, ip->chvc, nref, &ip->chvc->dnode);
522 			}
523 
524 			/* clean vnode information (list) */
525 			while (ip->chvc->v != (struct chfs_node_ref *)ip->chvc) {
526 				nref = ip->chvc->v;
527 				chfs_remove_and_obsolete(chmp, ip->chvc, nref, &ip->chvc->v);
528 			}
529 
530 			/* decrease number of links to parent */
531 			parent->chvc->nlink--;
532 
533 			mutex_exit(&chmp->chm_lock_vnocache);
534 			//TODO: if error
535 		}
536 	}
537 	mutex_exit(&chmp->chm_lock_mountfields);
538 
539 	return error;
540 }
541