HAMMER 12/many - buffer cache sync, buffer cache interactions, misc fixes.
[dragonfly.git] / sys / vfs / hammer / hammer_object.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/vfs/hammer/hammer_object.c,v 1.12 2007/12/30 08:49:20 dillon Exp $
35  */
36
37 #include "hammer.h"
38
39 static int hammer_mem_add(hammer_transaction_t trans,
40                              hammer_record_t record);
41 static int hammer_mem_lookup(hammer_cursor_t cursor, hammer_inode_t ip);
42 static int hammer_mem_first(hammer_cursor_t cursor, hammer_inode_t ip);
43 static void hammer_free_mem_record(hammer_record_t record);
44
45 /*
46  * Red-black tree support.
47  */
48 static int
49 hammer_rec_rb_compare(hammer_record_t rec1, hammer_record_t rec2)
50 {
51         if (rec1->rec.base.base.rec_type < rec2->rec.base.base.rec_type)
52                 return(-1);
53         if (rec1->rec.base.base.rec_type > rec2->rec.base.base.rec_type)
54                 return(1);
55
56         if (rec1->rec.base.base.key < rec2->rec.base.base.key)
57                 return(-1);
58         if (rec1->rec.base.base.key > rec2->rec.base.base.key)
59                 return(1);
60
61         if (rec1->rec.base.base.create_tid < rec2->rec.base.base.create_tid)
62                 return(-1);
63         if (rec1->rec.base.base.create_tid > rec2->rec.base.base.create_tid)
64                 return(1);
65         return(0);
66 }
67
68 static int
69 hammer_rec_compare(hammer_base_elm_t info, hammer_record_t rec)
70 {
71         if (info->rec_type < rec->rec.base.base.rec_type)
72                 return(-3);
73         if (info->rec_type > rec->rec.base.base.rec_type)
74                 return(3);
75
76         if (info->key < rec->rec.base.base.key)
77                 return(-2);
78         if (info->key > rec->rec.base.base.key)
79                 return(2);
80
81         /*
82          * This test has a number of special cases.  create_tid in key1 is
83          * the as-of transction id, and delete_tid in key1 is NOT USED.
84          *
85          * A key1->create_tid of 0 matches any record regardles of when
86          * it was created or destroyed.  0xFFFFFFFFFFFFFFFFULL should be
87          * used to search for the most current state of the object.
88          *
89          * key2->create_tid is a HAMMER record and will never be
90          * 0.   key2->delete_tid is the deletion transaction id or 0 if
91          * the record has not yet been deleted.
92          */
93         if (info->create_tid) {
94                 if (info->create_tid < rec->rec.base.base.create_tid)
95                         return(-1);
96                 if (rec->rec.base.base.delete_tid &&
97                     info->create_tid >= rec->rec.base.base.delete_tid) {
98                         return(1);
99                 }
100         }
101         return(0);
102 }
103
104 /*
105  * RB_SCAN comparison code for hammer_mem_first().  The argument order
106  * is reversed so the comparison result has to be negated.  key_beg and
107  * key_end are both range-inclusive.
108  *
109  * The creation timestamp can cause hammer_rec_compare() to return -1 or +1.
110  * These do not stop the scan.
111  *
112  * Localized deletions are not cached in-memory.
113  */
114 static
115 int
116 hammer_rec_scan_cmp(hammer_record_t rec, void *data)
117 {
118         hammer_cursor_t cursor = data;
119         int r;
120
121         r = hammer_rec_compare(&cursor->key_beg, rec);
122         if (r > 1)
123                 return(-1);
124         if (r == 0)
125                 return(0);
126         r = hammer_rec_compare(&cursor->key_end, rec);
127         if (r < -1)
128                 return(1);
129         return(0);
130 }
131
132 RB_GENERATE(hammer_rec_rb_tree, hammer_record, rb_node, hammer_rec_rb_compare);
133 RB_GENERATE_XLOOKUP(hammer_rec_rb_tree, INFO, hammer_record, rb_node,
134                     hammer_rec_compare, hammer_base_elm_t);
135
136 /*
137  * Allocate a record for the caller to finish filling in.  The record is
138  * returned referenced and locked.
139  */
140 hammer_record_t
141 hammer_alloc_mem_record(hammer_inode_t ip)
142 {
143         hammer_record_t record;
144
145         record = kmalloc(sizeof(*record), M_HAMMER, M_WAITOK|M_ZERO);
146         record->ip = ip;
147         hammer_ref(&record->lock);
148         hammer_lock_ex(&record->lock);
149         return (record);
150 }
151
152 /*
153  * Release a memory record.  If the record was marked for defered deletion,
154  * and no references remain, the record is physically destroyed.
155  */
156 void
157 hammer_rel_mem_record(struct hammer_record **recordp)
158 {
159         hammer_record_t rec;
160
161         if ((rec = *recordp) != NULL) {
162                 hammer_unref(&rec->lock);
163                 if (rec->lock.refs == 0) {
164                         if (rec->flags & HAMMER_RECF_DELETED)
165                                 hammer_free_mem_record(rec);
166                 }
167                 *recordp = NULL;
168         }
169 }
170
171 /*
172  * Drop a locked hammer in-memory record.  This function unlocks and
173  * dereferences the record.  If delete != 0 the record is marked for
174  * deletion.  Physical deletion only occurs when the last reference goes
175  * away.
176  */
177 void
178 hammer_drop_mem_record(hammer_record_t rec, int delete)
179 {
180         if (delete)
181                 rec->flags |= HAMMER_RECF_DELETED;
182         hammer_unlock(&rec->lock);
183         hammer_rel_mem_record(&rec);
184 }
185
186 /*
187  * Free a record.  Clean the structure up even though we are throwing it
188  * away as a sanity check.  The actual free operation is delayed while
189  * the record is referenced.  However, the record is removed from the RB
190  * tree immediately.
191  */
192 static void
193 hammer_free_mem_record(hammer_record_t record)
194 {
195         if (record->flags & HAMMER_RECF_ONRBTREE) {
196                 RB_REMOVE(hammer_rec_rb_tree, &record->ip->rec_tree, record);
197                 record->flags &= ~HAMMER_RECF_ONRBTREE;
198         }
199         if (record->lock.refs) {
200                 record->flags |= HAMMER_RECF_DELETED;
201                 return;
202         }
203         if (record->flags & HAMMER_RECF_ALLOCDATA) {
204                 kfree(record->data, M_HAMMER);
205                 record->flags &= ~HAMMER_RECF_ALLOCDATA;
206         }
207         record->data = NULL;
208         kfree(record, M_HAMMER);
209 }
210
211 /*
212  * Lookup an in-memory record given the key specified in the cursor.  Works
213  * just like hammer_btree_lookup() but operates on an inode's in-memory
214  * record list.
215  *
216  * The lookup must fail if the record is marked for deferred deletion.
217  */
218 static
219 int
220 hammer_mem_lookup(hammer_cursor_t cursor, hammer_inode_t ip)
221 {
222         int error;
223
224         if (cursor->iprec)
225                 hammer_rel_mem_record(&cursor->iprec);
226         if (cursor->ip) {
227                 hammer_rec_rb_tree_scan_info_done(&cursor->scan,
228                                                   &cursor->ip->rec_tree);
229         }
230         cursor->ip = ip;
231         hammer_rec_rb_tree_scan_info_link(&cursor->scan, &ip->rec_tree);
232         cursor->scan.node = NULL;
233         cursor->iprec = hammer_rec_rb_tree_RB_LOOKUP_INFO(
234                                 &ip->rec_tree, &cursor->key_beg);
235         if (cursor->iprec == NULL) {
236                 error = ENOENT;
237         } else {
238                 hammer_ref(&cursor->iprec->lock);
239                 error = 0;
240         }
241         return(error);
242 }
243
244 /*
245  * hammer_mem_first() - locate the first in-memory record matching the
246  * cursor.
247  *
248  * The RB_SCAN function we use is designed as a callback.  We terminate it
249  * (return -1) as soon as we get a match.
250  */
251 static
252 int
253 hammer_rec_scan_callback(hammer_record_t rec, void *data)
254 {
255         hammer_cursor_t cursor = data;
256
257         /*
258          * Skip if not visible due to our as-of TID
259          */
260         if (cursor->key_beg.create_tid) {
261                 if (cursor->key_beg.create_tid < rec->rec.base.base.create_tid)
262                         return(0);
263                 if (rec->rec.base.base.delete_tid &&
264                     cursor->key_beg.create_tid >=
265                      rec->rec.base.base.delete_tid) {
266                         return(0);
267                 }
268         }
269
270         /*
271          * Return the first matching record and stop the scan
272          */
273         if (cursor->iprec == NULL) {
274                 cursor->iprec = rec;
275                 hammer_ref(&rec->lock);
276                 return(-1);
277         }
278         return(0);
279 }
280
281 static
282 int
283 hammer_mem_first(hammer_cursor_t cursor, hammer_inode_t ip)
284 {
285         if (cursor->iprec)
286                 hammer_rel_mem_record(&cursor->iprec);
287         if (cursor->ip) {
288                 hammer_rec_rb_tree_scan_info_done(&cursor->scan,
289                                                   &cursor->ip->rec_tree);
290         }
291         cursor->ip = ip;
292         hammer_rec_rb_tree_scan_info_link(&cursor->scan, &ip->rec_tree);
293         cursor->scan.node = NULL;
294         hammer_rec_rb_tree_RB_SCAN(&ip->rec_tree, hammer_rec_scan_cmp,
295                                    hammer_rec_scan_callback, cursor);
296
297         /*
298          * Adjust scan.node and keep it linked into the RB-tree so we can
299          * hold the cursor through third party modifications of the RB-tree.
300          */
301         if (cursor->iprec) {
302                 cursor->scan.node = hammer_rec_rb_tree_RB_NEXT(cursor->iprec);
303                 return(0);
304         }
305         return(ENOENT);
306 }
307
308 void
309 hammer_mem_done(hammer_cursor_t cursor)
310 {
311         if (cursor->ip) {
312                 hammer_rec_rb_tree_scan_info_done(&cursor->scan,
313                                                   &cursor->ip->rec_tree);
314                 cursor->ip = NULL;
315         }
316         if (cursor->iprec)
317                 hammer_rel_mem_record(&cursor->iprec);
318 }
319
320 /************************************************************************
321  *                   HAMMER IN-MEMORY RECORD FUNCTIONS                  *
322  ************************************************************************
323  *
324  * These functions manipulate in-memory records.  Such records typically
325  * exist prior to being committed to disk or indexed via the on-disk B-Tree.
326  */
327
328 /*
329  * Add a directory entry (dip,ncp) which references inode (ip).
330  *
331  * Note that the low 32 bits of the namekey are set temporarily to create
332  * a unique in-memory record, and may be modified a second time when the
333  * record is synchronized to disk.  In particular, the low 32 bits cannot be
334  * all 0's when synching to disk, which is not handled here.
335  */
336 int
337 hammer_ip_add_directory(struct hammer_transaction *trans,
338                      struct hammer_inode *dip, struct namecache *ncp,
339                      struct hammer_inode *ip)
340 {
341         hammer_record_t record;
342         int error;
343         int bytes;
344
345         record = hammer_alloc_mem_record(dip);
346
347         bytes = ncp->nc_nlen;   /* NOTE: terminating \0 is NOT included */
348         if (++trans->hmp->namekey_iterator == 0)
349                 ++trans->hmp->namekey_iterator;
350
351         record->rec.entry.base.base.obj_id = dip->obj_id;
352         record->rec.entry.base.base.key =
353                 hammer_directory_namekey(ncp->nc_name, bytes);
354         record->rec.entry.base.base.key += trans->hmp->namekey_iterator;
355         record->rec.entry.base.base.create_tid = trans->tid;
356         record->rec.entry.base.base.rec_type = HAMMER_RECTYPE_DIRENTRY;
357         record->rec.entry.base.base.obj_type = ip->ino_rec.base.base.obj_type;
358         record->rec.entry.obj_id = ip->obj_id;
359         if (bytes <= sizeof(record->rec.entry.den_name)) {
360                 record->data = (void *)record->rec.entry.den_name;
361                 record->flags |= HAMMER_RECF_EMBEDDED_DATA;
362         } else {
363                 record->data = kmalloc(bytes, M_HAMMER, M_WAITOK);
364                 record->flags |= HAMMER_RECF_ALLOCDATA;
365         }
366         bcopy(ncp->nc_name, record->data, bytes);
367         record->rec.entry.base.data_len = bytes;
368         ++ip->ino_rec.ino_nlinks;
369         hammer_modify_inode(trans, ip,
370                             HAMMER_INODE_RDIRTY | HAMMER_INODE_TID);
371         error = hammer_mem_add(trans, record);
372         return(error);
373 }
374
375 /*
376  * Delete the directory entry and update the inode link count.  The
377  * cursor must be seeked to the directory entry record being deleted.
378  *
379  * NOTE: HAMMER_CURSOR_DELETE may not have been set.  XXX remove flag.
380  */
381 int
382 hammer_ip_del_directory(struct hammer_transaction *trans,
383                      hammer_cursor_t cursor, struct hammer_inode *dip,
384                      struct hammer_inode *ip)
385 {
386         int error;
387
388         error = hammer_ip_delete_record(cursor, trans->tid);
389
390         /*
391          * One less link.  The file may still be open in the OS even after
392          * all links have gone away so we don't destroy the inode's data
393          * here.
394          */
395         if (error == 0) {
396                 --ip->ino_rec.ino_nlinks;
397                 hammer_modify_inode(trans, ip,
398                                     HAMMER_INODE_RDIRTY | HAMMER_INODE_TID);
399                 if (ip->vp == NULL || (ip->vp->v_flag & VINACTIVE))
400                         hammer_sync_inode(ip, MNT_NOWAIT, 1);
401
402         }
403         return(error);
404 }
405
406 /*
407  * Add a record to an inode.
408  *
409  * The caller must allocate the record with hammer_alloc_mem_record(ip) and
410  * initialize the following additional fields:
411  *
412  * record->rec.entry.base.base.key
413  * record->rec.entry.base.base.rec_type
414  * record->rec.entry.base.base.data_len
415  * record->data         (a copy will be kmalloc'd if not embedded)
416  */
417 int
418 hammer_ip_add_record(struct hammer_transaction *trans, hammer_record_t record)
419 {
420         hammer_inode_t ip = record->ip;
421         int error;
422         int bytes;
423         void *data;
424
425         record->rec.base.base.obj_id = ip->obj_id;
426         record->rec.base.base.create_tid = trans->tid;
427         record->rec.base.base.obj_type = ip->ino_rec.base.base.obj_type;
428         bytes = record->rec.base.data_len;
429
430         if (record->data) {
431                 if ((char *)record->data < (char *)&record->rec ||
432                     (char *)record->data >= (char *)(&record->rec + 1)) {
433                         data = kmalloc(bytes, M_HAMMER, M_WAITOK);
434                         record->flags |= HAMMER_RECF_ALLOCDATA;
435                         bcopy(record->data, data, bytes);
436                         record->data = data;
437                 } else {
438                         record->flags |= HAMMER_RECF_EMBEDDED_DATA;
439                 }
440         }
441         hammer_modify_inode(trans, ip,
442                             HAMMER_INODE_RDIRTY | HAMMER_INODE_TID);
443         error = hammer_mem_add(trans, record);
444         return(error);
445 }
446
447 /*
448  * Sync data from a buffer cache buffer (typically) to the filesystem.  This
449  * is called via the strategy called from a cached data source.  This code
450  * is responsible for actually writing a data record out to the disk.
451  */
452 int
453 hammer_ip_sync_data(hammer_transaction_t trans, hammer_inode_t ip,
454                        int64_t offset, void *data, int bytes,
455                        struct hammer_cursor **spike)
456 {
457         struct hammer_cursor cursor;
458         hammer_record_ondisk_t rec;
459         union hammer_btree_elm elm;
460         void *bdata;
461         int error;
462
463         error = hammer_init_cursor_ip(&cursor, ip);
464         if (error)
465                 return(error);
466         cursor.key_beg.obj_id = ip->obj_id;
467         cursor.key_beg.key = offset + bytes;
468         cursor.key_beg.create_tid = trans->tid;
469         cursor.key_beg.delete_tid = 0;
470         cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
471         cursor.flags = HAMMER_CURSOR_INSERT;
472
473         /*
474          * Issue a lookup to position the cursor and locate the cluster
475          */
476         error = hammer_btree_lookup(&cursor);
477         if (error == 0) {
478                 kprintf("hammer_ip_sync_data: duplicate data at (%lld,%d)\n",
479                         offset, bytes);
480                 hammer_print_btree_elm(&cursor.node->ondisk->elms[cursor.index],
481                                        HAMMER_BTREE_TYPE_LEAF, cursor.index);
482                 error = EIO;
483         }
484         if (error != ENOENT)
485                 goto done;
486
487         /*
488          * Allocate record and data space now that we know which cluster
489          * the B-Tree node ended up in.
490          */
491         bdata = hammer_alloc_data(cursor.node->cluster, bytes, &error,
492                                   &cursor.data_buffer);
493         if (bdata == NULL)
494                 goto done;
495         rec = hammer_alloc_record(cursor.node->cluster, &error,
496                                   &cursor.record_buffer);
497         if (rec == NULL)
498                 goto fail1;
499
500         /*
501          * Fill everything in and insert our B-Tree node.
502          */
503         hammer_modify_buffer(cursor.record_buffer);
504         rec->base.base = cursor.key_beg;
505         rec->base.data_crc = crc32(data, bytes);
506         rec->base.rec_id = 0;   /* XXX */
507         rec->base.data_offset = hammer_bclu_offset(cursor.data_buffer, bdata);
508         rec->base.data_len = bytes;
509         hammer_modify_buffer_done(cursor.record_buffer);
510
511         hammer_modify_buffer(cursor.data_buffer);
512         bcopy(data, bdata, bytes);
513         hammer_modify_buffer_done(cursor.data_buffer);
514
515         elm.leaf.base = cursor.key_beg;
516         elm.leaf.rec_offset = hammer_bclu_offset(cursor.record_buffer, rec);
517         elm.leaf.data_offset = rec->base.data_offset;
518         elm.leaf.data_len = bytes;
519         elm.leaf.data_crc = rec->base.data_crc;
520
521         error = hammer_btree_insert(&cursor, &elm);
522         if (error == 0)
523                 goto done;
524
525         hammer_free_record_ptr(cursor.record_buffer, rec);
526 fail1:
527         hammer_free_data_ptr(cursor.data_buffer, bdata, bytes);
528 done:
529         /*
530          * If ENOSPC in cluster fill in the spike structure and return
531          * ENOSPC.
532          */
533         if (error == ENOSPC)
534                 hammer_load_spike(&cursor, spike);
535         hammer_done_cursor(&cursor);
536         return(error);
537 }
538
539 /*
540  * Sync an in-memory record to the disk.  this is typically called via fsync
541  * from a cached record source.  This code is responsible for actually
542  * writing a record out to the disk.
543  */
544 int
545 hammer_ip_sync_record(hammer_record_t record, struct hammer_cursor **spike)
546 {
547         struct hammer_cursor cursor;
548         hammer_record_ondisk_t rec;
549         union hammer_btree_elm elm;
550         void *bdata;
551         int error;
552
553         error = hammer_init_cursor_ip(&cursor, record->ip);
554         if (error)
555                 return(error);
556         cursor.key_beg = record->rec.base.base;
557         cursor.flags = HAMMER_CURSOR_INSERT;
558
559         /*
560          * Issue a lookup to position the cursor and locate the cluster.  The
561          * target key should not exist.
562          *
563          * If we run out of space trying to adjust the B-Tree for the
564          * insert, re-lookup without the insert flag so the cursor
565          * is properly positioned for the spike.
566          */
567         error = hammer_btree_lookup(&cursor);
568         if (error == 0) {
569                 kprintf("hammer_ip_sync_record: duplicate rec at (%016llx)\n",
570                         record->rec.base.base.key);
571                 error = EIO;
572         }
573         if (error != ENOENT)
574                 goto done;
575
576         /*
577          * Allocate record and data space now that we know which cluster
578          * the B-Tree node ended up in.
579          */
580         if (record->data == NULL ||
581             (record->flags & HAMMER_RECF_EMBEDDED_DATA)) {
582                 bdata = record->data;
583         } else {
584                 bdata = hammer_alloc_data(cursor.node->cluster,
585                                           record->rec.base.data_len, &error,
586                                           &cursor.data_buffer);
587                 if (bdata == NULL)
588                         goto done;
589         }
590         rec = hammer_alloc_record(cursor.node->cluster, &error,
591                                   &cursor.record_buffer);
592         if (rec == NULL)
593                 goto fail1;
594
595         /*
596          * Fill everything in and insert our B-Tree node.
597          *
598          * XXX assign rec_id here
599          */
600         hammer_modify_buffer(cursor.record_buffer);
601         *rec = record->rec;
602         if (bdata) {
603                 rec->base.data_crc = crc32(record->data,
604                                            record->rec.base.data_len);
605                 if (record->flags & HAMMER_RECF_EMBEDDED_DATA) {
606                         /*
607                          * Data embedded in record
608                          */
609                         rec->base.data_offset = ((char *)bdata -
610                                                  (char *)&record->rec);
611                         KKASSERT(rec->base.data_offset >= 0 && 
612                                  rec->base.data_offset + rec->base.data_len <=
613                                   sizeof(*rec));
614                         rec->base.data_offset += hammer_bclu_offset(cursor.record_buffer, rec);
615                 } else {
616                         /*
617                          * Data separate from record
618                          */
619                         rec->base.data_offset = hammer_bclu_offset(cursor.data_buffer,bdata);
620                         hammer_modify_buffer(cursor.data_buffer);
621                         bcopy(record->data, bdata, rec->base.data_len);
622                         hammer_modify_buffer_done(cursor.data_buffer);
623                 }
624         }
625         rec->base.rec_id = 0;   /* XXX */
626         hammer_modify_buffer_done(cursor.record_buffer);
627
628         elm.leaf.base = cursor.key_beg;
629         elm.leaf.rec_offset = hammer_bclu_offset(cursor.record_buffer, rec);
630         elm.leaf.data_offset = rec->base.data_offset;
631         elm.leaf.data_len = rec->base.data_len;
632         elm.leaf.data_crc = rec->base.data_crc;
633
634         error = hammer_btree_insert(&cursor, &elm);
635         if (error == 0)
636                 goto done;
637
638         hammer_free_record_ptr(cursor.record_buffer, rec);
639 fail1:
640         if (record->data && (record->flags & HAMMER_RECF_EMBEDDED_DATA) == 0) {
641                 hammer_free_data_ptr(cursor.data_buffer, bdata,
642                                      record->rec.base.data_len);
643         }
644 done:
645         /*
646          * If ENOSPC in cluster fill in the spike structure and return
647          * ENOSPC.
648          */
649         if (error == ENOSPC)
650                 hammer_load_spike(&cursor, spike);
651         hammer_done_cursor(&cursor);
652         return(error);
653 }
654
655 /*
656  * Write out a record using the specified cursor.  The caller does not have
657  * to seek the cursor.  The flags are used to determine whether the data
658  * (if any) is embedded in the record or not.
659  *
660  * The target cursor will be modified by this call.  Note in particular
661  * that HAMMER_CURSOR_INSERT is set.
662  */
663 int
664 hammer_write_record(hammer_cursor_t cursor, hammer_record_ondisk_t orec,
665                     void *data, int cursor_flags)
666 {
667         union hammer_btree_elm elm;
668         hammer_record_ondisk_t nrec;
669         void *bdata;
670         int error;
671
672         cursor->key_beg = orec->base.base;
673         cursor->flags |= HAMMER_CURSOR_INSERT;
674
675         /*
676          * Issue a lookup to position the cursor and locate the cluster.  The
677          * target key should not exist.
678          *
679          * If we run out of space trying to adjust the B-Tree for the
680          * insert, re-lookup without the insert flag so the cursor
681          * is properly positioned for the spike.
682          */
683         error = hammer_btree_lookup(cursor);
684         if (error == 0) {
685                 kprintf("hammer_ip_sync_record: duplicate rec at (%016llx)\n",
686                         orec->base.base.key);
687                 error = EIO;
688         }
689         if (error != ENOENT)
690                 goto done;
691
692         /*
693          * Allocate record and data space now that we know which cluster
694          * the B-Tree node ended up in.
695          */
696         if (data == NULL ||
697             (cursor_flags & HAMMER_RECF_EMBEDDED_DATA)) {
698                 bdata = data;
699         } else {
700                 bdata = hammer_alloc_data(cursor->node->cluster,
701                                           orec->base.data_len, &error,
702                                           &cursor->data_buffer);
703                 if (bdata == NULL)
704                         goto done;
705         }
706         nrec = hammer_alloc_record(cursor->node->cluster, &error,
707                                   &cursor->record_buffer);
708         if (nrec == NULL)
709                 goto fail1;
710
711         /*
712          * Fill everything in and insert our B-Tree node.
713          *
714          * XXX assign rec_id here
715          */
716         hammer_modify_buffer(cursor->record_buffer);
717         *nrec = *orec;
718         nrec->base.data_offset = 0;
719         if (bdata) {
720                 nrec->base.data_crc = crc32(bdata, nrec->base.data_len);
721                 if (cursor_flags & HAMMER_RECF_EMBEDDED_DATA) {
722                         /*
723                          * Data embedded in record
724                          */
725                         nrec->base.data_offset = ((char *)bdata - (char *)orec);
726                         KKASSERT(nrec->base.data_offset >= 0 && 
727                                  nrec->base.data_offset + nrec->base.data_len <
728                                   sizeof(*nrec));
729                         nrec->base.data_offset += hammer_bclu_offset(cursor->record_buffer, nrec);
730                 } else {
731                         /*
732                          * Data separate from record
733                          */
734                         nrec->base.data_offset = hammer_bclu_offset(cursor->data_buffer, bdata);
735                         hammer_modify_buffer(cursor->data_buffer);
736                         bcopy(data, bdata, nrec->base.data_len);
737                         hammer_modify_buffer_done(cursor->data_buffer);
738                 }
739         }
740         nrec->base.rec_id = 0;  /* XXX */
741         hammer_modify_buffer_done(cursor->record_buffer);
742
743         elm.leaf.base = nrec->base.base;
744         elm.leaf.rec_offset = hammer_bclu_offset(cursor->record_buffer, nrec);
745         elm.leaf.data_offset = nrec->base.data_offset;
746         elm.leaf.data_len = nrec->base.data_len;
747         elm.leaf.data_crc = nrec->base.data_crc;
748
749         error = hammer_btree_insert(cursor, &elm);
750         if (error == 0)
751                 goto done;
752
753         hammer_free_record_ptr(cursor->record_buffer, nrec);
754 fail1:
755         if (data && (cursor_flags & HAMMER_RECF_EMBEDDED_DATA) == 0) {
756                 hammer_free_data_ptr(cursor->data_buffer, bdata,
757                                      orec->base.data_len);
758         }
759 done:
760         /* leave cursor intact */
761         return(error);
762 }
763
764 /*
765  * Add the record to the inode's rec_tree.  The low 32 bits of a directory
766  * entry's key is used to deal with hash collisions in the upper 32 bits.
767  * A unique 64 bit key is generated in-memory and may be regenerated a
768  * second time when the directory record is flushed to the on-disk B-Tree.
769  *
770  * A locked and referenced record is passed to this function.  This function
771  * eats the lock and reference.
772  */
773 static
774 int
775 hammer_mem_add(struct hammer_transaction *trans, hammer_record_t record)
776 {
777         while (RB_INSERT(hammer_rec_rb_tree, &record->ip->rec_tree, record)) {
778                 if (record->rec.base.base.rec_type != HAMMER_RECTYPE_DIRENTRY){
779                         hammer_drop_mem_record(record, 1);
780                         return (EEXIST);
781                 }
782                 if (++trans->hmp->namekey_iterator == 0)
783                         ++trans->hmp->namekey_iterator;
784                 record->rec.base.base.key &= ~(0xFFFFFFFFLL);
785                 record->rec.base.base.key |= trans->hmp->namekey_iterator;
786         }
787         record->flags |= HAMMER_RECF_ONRBTREE;
788         hammer_drop_mem_record(record, 0);
789         return(0);
790 }
791
792 /************************************************************************
793  *                   HAMMER INODE MERGED-RECORD FUNCTIONS               *
794  ************************************************************************
795  *
796  * These functions augment the B-Tree scanning functions in hammer_btree.c
797  * by merging in-memory records with on-disk records.
798  */
799
800 /*
801  * Locate a particular record either in-memory or on-disk.
802  *
803  * NOTE: This is basically a standalone routine, hammer_ip_next() may
804  * NOT be called to iterate results.
805  */
806 int
807 hammer_ip_lookup(hammer_cursor_t cursor, struct hammer_inode *ip)
808 {
809         int error;
810
811         /*
812          * If the element is in-memory return it without searching the
813          * on-disk B-Tree
814          */
815         error = hammer_mem_lookup(cursor, ip);
816         if (error == 0) {
817                 cursor->record = &cursor->iprec->rec;
818                 return(error);
819         }
820         if (error != ENOENT)
821                 return(error);
822
823         /*
824          * If the inode has on-disk components search the on-disk B-Tree.
825          */
826         if ((ip->flags & HAMMER_INODE_ONDISK) == 0)
827                 return(error);
828         error = hammer_btree_lookup(cursor);
829         if (error == 0)
830                 error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_RECORD);
831         return(error);
832 }
833
834 /*
835  * Locate the first record within the cursor's key_beg/key_end range,
836  * restricted to a particular inode.  0 is returned on success, ENOENT
837  * if no records matched the requested range, or some other error.
838  *
839  * When 0 is returned hammer_ip_next() may be used to iterate additional
840  * records within the requested range.
841  */
842 int
843 hammer_ip_first(hammer_cursor_t cursor, struct hammer_inode *ip)
844 {
845         int error;
846
847         /*
848          * Clean up fields and setup for merged scan
849          */
850         cursor->flags &= ~HAMMER_CURSOR_DELBTREE;
851         cursor->flags |= HAMMER_CURSOR_ATEDISK | HAMMER_CURSOR_ATEMEM;
852         cursor->flags |= HAMMER_CURSOR_DISKEOF | HAMMER_CURSOR_MEMEOF;
853         if (cursor->iprec)
854                 hammer_rel_mem_record(&cursor->iprec);
855
856         /*
857          * Search the on-disk B-Tree.  hammer_btree_lookup() only does an
858          * exact lookup so if we get ENOENT we have to call the iterate
859          * function to validate the first record after the begin key.
860          *
861          * The ATEDISK flag is used by hammer_btree_iterate to determine
862          * whether it must index forwards or not.  It is also used here
863          * to select the next record from in-memory or on-disk.
864          */
865         if (ip->flags & HAMMER_INODE_ONDISK) {
866                 error = hammer_btree_lookup(cursor);
867                 if (error == ENOENT) {
868                         cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
869                         error = hammer_btree_iterate(cursor);
870                 }
871                 if (error && error != ENOENT) 
872                         return(error);
873                 if (error == 0) {
874                         cursor->flags &= ~HAMMER_CURSOR_DISKEOF;
875                         cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
876                 } else {
877                         cursor->flags |= HAMMER_CURSOR_ATEDISK;
878                 }
879         }
880
881         /*
882          * Search the in-memory record list (Red-Black tree).  Unlike the
883          * B-Tree search, mem_first checks for records in the range.
884          */
885         error = hammer_mem_first(cursor, ip);
886         if (error && error != ENOENT)
887                 return(error);
888         if (error == 0) {
889                 cursor->flags &= ~HAMMER_CURSOR_MEMEOF;
890                 cursor->flags &= ~HAMMER_CURSOR_ATEMEM;
891         }
892
893         /*
894          * This will return the first matching record.
895          */
896         return(hammer_ip_next(cursor));
897 }
898
899 /*
900  * Retrieve the next record in a merged iteration within the bounds of the
901  * cursor.  This call may be made multiple times after the cursor has been
902  * initially searched with hammer_ip_first().
903  *
904  * 0 is returned on success, ENOENT if no further records match the
905  * requested range, or some other error code is returned.
906  */
907 int
908 hammer_ip_next(hammer_cursor_t cursor)
909 {
910         hammer_btree_elm_t elm;
911         hammer_record_t rec;
912         int error;
913         int r;
914
915         /*
916          * Load the current on-disk and in-memory record.  If we ate any
917          * records we have to get the next one. 
918          *
919          * If we deleted the last on-disk record we had scanned ATEDISK will
920          * be clear and DELBTREE will be set, forcing a call to iterate. The
921          * fact that ATEDISK is clear causes iterate to re-test the 'current'
922          * element.  If ATEDISK is set, iterate will skip the 'current'
923          * element.
924          *
925          * Get the next on-disk record
926          */
927         if (cursor->flags & (HAMMER_CURSOR_ATEDISK|HAMMER_CURSOR_DELBTREE)) {
928                 if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
929                         error = hammer_btree_iterate(cursor);
930                         if (error == 0)
931                                 cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
932                         else
933                                 cursor->flags |= HAMMER_CURSOR_DISKEOF |
934                                                  HAMMER_CURSOR_ATEDISK;
935                 }
936         }
937
938         /*
939          * Get the next in-memory record.  The record can be ripped out
940          * of the RB tree so we maintain a scan_info structure to track
941          * the next node.
942          *
943          * hammer_rec_scan_cmp:  Is the record still in our general range,
944          *                       (non-inclusive of snapshot exclusions)?
945          * hammer_rec_scan_callback: Is the record in our snapshot?
946          */
947         if (cursor->flags & HAMMER_CURSOR_ATEMEM) {
948                 if ((cursor->flags & HAMMER_CURSOR_MEMEOF) == 0) {
949                         hammer_rel_mem_record(&cursor->iprec);
950                         rec = cursor->scan.node;        /* next node */
951                         while (rec) {
952                                 if (hammer_rec_scan_cmp(rec, cursor) != 0)
953                                         break;
954                                 if (hammer_rec_scan_callback(rec, cursor) != 0)
955                                         break;
956                                 rec = hammer_rec_rb_tree_RB_NEXT(rec);
957                         }
958                         if (cursor->iprec) {
959                                 cursor->flags &= ~HAMMER_CURSOR_ATEMEM;
960                                 hammer_ref(&cursor->iprec->lock);
961                                 cursor->scan.node =
962                                         hammer_rec_rb_tree_RB_NEXT(rec);
963                         } else {
964                                 cursor->flags |= HAMMER_CURSOR_MEMEOF;
965                         }
966                 }
967         }
968
969         /*
970          * Extract either the disk or memory record depending on their
971          * relative position.
972          */
973         error = 0;
974         switch(cursor->flags & (HAMMER_CURSOR_ATEDISK | HAMMER_CURSOR_ATEMEM)) {
975         case 0:
976                 /*
977                  * Both entries valid
978                  */
979                 elm = &cursor->node->ondisk->elms[cursor->index];
980                 r = hammer_btree_cmp(&elm->base, &cursor->iprec->rec.base.base);
981                 if (r < 0) {
982                         error = hammer_btree_extract(cursor,
983                                                      HAMMER_CURSOR_GET_RECORD);
984                         cursor->flags |= HAMMER_CURSOR_ATEDISK;
985                         break;
986                 }
987                 /* fall through to the memory entry */
988         case HAMMER_CURSOR_ATEDISK:
989                 /*
990                  * Only the memory entry is valid
991                  */
992                 cursor->record = &cursor->iprec->rec;
993                 cursor->flags |= HAMMER_CURSOR_ATEMEM;
994                 break;
995         case HAMMER_CURSOR_ATEMEM:
996                 /*
997                  * Only the disk entry is valid
998                  */
999                 error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_RECORD);
1000                 cursor->flags |= HAMMER_CURSOR_ATEDISK;
1001                 break;
1002         default:
1003                 /*
1004                  * Neither entry is valid
1005                  *
1006                  * XXX error not set properly
1007                  */
1008                 cursor->record = NULL;
1009                 error = ENOENT;
1010                 break;
1011         }
1012         return(error);
1013 }
1014
1015 /*
1016  * Resolve the cursor->data pointer for the current cursor position in
1017  * a merged iteration.
1018  */
1019 int
1020 hammer_ip_resolve_data(hammer_cursor_t cursor)
1021 {
1022         int error;
1023
1024         if (cursor->iprec && cursor->record == &cursor->iprec->rec) {
1025                 cursor->data = cursor->iprec->data;
1026                 error = 0;
1027         } else {
1028                 error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_DATA);
1029         }
1030         return(error);
1031 }
1032
1033 /*
1034  * Delete all records within the specified range for inode ip.
1035  *
1036  * NOTE: An unaligned range will cause new records to be added to cover
1037  * the edge cases. (XXX not implemented yet).
1038  *
1039  * NOTE: ran_end is inclusive (e.g. 0,1023 instead of 0,1024).
1040  *
1041  * NOTE: Record keys for regular file data have to be special-cased since
1042  * they indicate the end of the range (key = base + bytes).
1043  *
1044  * NOTE: The spike structure must be filled in if we return ENOSPC.
1045  */
1046 int
1047 hammer_ip_delete_range(hammer_transaction_t trans, hammer_inode_t ip,
1048                        int64_t ran_beg, int64_t ran_end,
1049                        struct hammer_cursor **spike)
1050 {
1051         struct hammer_cursor cursor;
1052         hammer_record_ondisk_t rec;
1053         hammer_base_elm_t base;
1054         int error;
1055         int64_t off;
1056
1057         hammer_init_cursor_ip(&cursor, ip);
1058
1059         cursor.key_beg.obj_id = ip->obj_id;
1060         cursor.key_beg.create_tid = ip->obj_asof;
1061         cursor.key_beg.delete_tid = 0;
1062         cursor.key_beg.obj_type = 0;
1063
1064         cursor.key_end = cursor.key_beg;
1065         if (ip->ino_rec.base.base.obj_type == HAMMER_OBJTYPE_DBFILE) {
1066                 cursor.key_beg.key = ran_beg;
1067                 cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
1068                 cursor.key_end.rec_type = HAMMER_RECTYPE_DB;
1069                 cursor.key_end.key = ran_end;
1070         } else {
1071                 /*
1072                  * The key in the B-Tree is (base+bytes), so the first possible
1073                  * matching key is ran_beg + 1.
1074                  */
1075                 int64_t tmp64;
1076
1077                 cursor.key_beg.key = ran_beg + 1;
1078                 cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
1079                 cursor.key_end.rec_type = HAMMER_RECTYPE_DATA;
1080
1081                 tmp64 = ran_end + MAXPHYS + 1;  /* work around GCC-4 bug */
1082                 if (tmp64 < ran_end)
1083                         cursor.key_end.key = 0x7FFFFFFFFFFFFFFFLL;
1084                 else
1085                         cursor.key_end.key = ran_end + MAXPHYS + 1;
1086         }
1087         cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
1088
1089         error = hammer_ip_first(&cursor, ip);
1090
1091         /*
1092          * Iterate through matching records and mark them as deleted.
1093          */
1094         while (error == 0) {
1095                 rec = cursor.record;
1096                 base = &rec->base.base;
1097
1098                 KKASSERT(base->delete_tid == 0);
1099
1100                 /*
1101                  * There may be overlap cases for regular file data.  Also
1102                  * remember the key for a regular file record is the offset
1103                  * of the last byte of the record (base + len - 1), NOT the
1104                  * base offset.
1105                  */
1106 #if 0
1107                 kprintf("delete_range rec_type %02x\n", base->rec_type);
1108 #endif
1109                 if (base->rec_type == HAMMER_RECTYPE_DATA) {
1110 #if 0
1111                         kprintf("delete_range loop key %016llx\n",
1112                                 base->key - rec->base.data_len);
1113 #endif
1114                         off = base->key - rec->base.data_len;
1115                         /*
1116                          * Check the left edge case.  We currently do not
1117                          * split existing records.
1118                          */
1119                         if (off < ran_beg) {
1120                                 panic("hammer left edge case %016llx %d\n",
1121                                         base->key, rec->base.data_len);
1122                         }
1123
1124                         /*
1125                          * Check the right edge case.  Note that the
1126                          * record can be completely out of bounds, which
1127                          * terminates the search.
1128                          *
1129                          * base->key is exclusive of the right edge while
1130                          * ran_end is inclusive of the right edge.  The
1131                          * (key - data_len) left boundary is inclusive.
1132                          *
1133                          * XXX theory-check this test at some point, are
1134                          * we missing a + 1 somewhere?  Note that ran_end
1135                          * could overflow.
1136                          */
1137                         if (base->key > ran_end) {
1138                                 if (base->key - rec->base.data_len > ran_end) {
1139                                         kprintf("right edge OOB\n");
1140                                         break;
1141                                 }
1142                                 panic("hammer right edge case\n");
1143                         }
1144                 }
1145
1146                 /*
1147                  * Mark the record and B-Tree entry as deleted.  This will
1148                  * also physically delete the B-Tree entry, record, and
1149                  * data if the retention policy dictates.  The function
1150                  * will set HAMMER_CURSOR_DELBTREE which hammer_ip_next()
1151                  * uses to perform a fixup.
1152                  */
1153                 error = hammer_ip_delete_record(&cursor, trans->tid);
1154                 if (error)
1155                         break;
1156                 error = hammer_ip_next(&cursor);
1157         }
1158         hammer_done_cursor(&cursor);
1159         if (error == ENOENT)
1160                 error = 0;
1161         return(error);
1162 }
1163
1164 int
1165 hammer_ip_delete_range_all(hammer_transaction_t trans, hammer_inode_t ip)
1166 {
1167         struct hammer_cursor cursor;
1168         hammer_record_ondisk_t rec;
1169         hammer_base_elm_t base;
1170         int error;
1171
1172         hammer_init_cursor_ip(&cursor, ip);
1173
1174         cursor.key_beg.obj_id = ip->obj_id;
1175         cursor.key_beg.create_tid = ip->obj_asof;
1176         cursor.key_beg.delete_tid = 0;
1177         cursor.key_beg.obj_type = 0;
1178         cursor.key_beg.rec_type = 0;
1179         cursor.key_beg.key = HAMMER_MIN_KEY;
1180
1181         cursor.key_end = cursor.key_beg;
1182         cursor.key_end.rec_type = 0xFFFF;
1183         cursor.key_end.key = HAMMER_MAX_KEY;
1184
1185         cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
1186
1187         error = hammer_ip_first(&cursor, ip);
1188
1189         /*
1190          * Iterate through matching records and mark them as deleted.
1191          */
1192         while (error == 0) {
1193                 rec = cursor.record;
1194                 base = &rec->base.base;
1195
1196                 KKASSERT(base->delete_tid == 0);
1197
1198                 /*
1199                  * Mark the record and B-Tree entry as deleted.  This will
1200                  * also physically delete the B-Tree entry, record, and
1201                  * data if the retention policy dictates.  The function
1202                  * will set HAMMER_CURSOR_DELBTREE which hammer_ip_next()
1203                  * uses to perform a fixup.
1204                  */
1205                 error = hammer_ip_delete_record(&cursor, trans->tid);
1206                 if (error)
1207                         break;
1208                 error = hammer_ip_next(&cursor);
1209         }
1210         hammer_done_cursor(&cursor);
1211         if (error == ENOENT)
1212                 error = 0;
1213         return(error);
1214 }
1215
1216 /*
1217  * Delete the record at the current cursor
1218  */
1219 int
1220 hammer_ip_delete_record(hammer_cursor_t cursor, hammer_tid_t tid)
1221 {
1222         hammer_btree_elm_t elm;
1223         hammer_mount_t hmp;
1224         int error;
1225
1226         /*
1227          * In-memory (unsynchronized) records can simply be freed.
1228          */
1229         cursor->flags &= ~HAMMER_CURSOR_DELBTREE;
1230         if (cursor->record == &cursor->iprec->rec) {
1231                 hammer_free_mem_record(cursor->iprec); /* XXX */
1232                 return(0);
1233         }
1234
1235         /*
1236          * On-disk records are marked as deleted by updating their delete_tid.
1237          */
1238         error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_RECORD);
1239         elm = NULL;
1240         hmp = cursor->node->cluster->volume->hmp;
1241
1242         if (error == 0) {
1243                 hammer_modify_buffer(cursor->record_buffer);
1244                 cursor->record->base.base.delete_tid = tid;
1245                 hammer_modify_buffer_done(cursor->record_buffer);
1246                 hammer_modify_node(cursor->node);
1247                 elm = &cursor->node->ondisk->elms[cursor->index];
1248                 elm->leaf.base.delete_tid = tid;
1249                 hammer_modify_node_done(cursor->node);
1250         }
1251
1252         /*
1253          * If we were mounted with the nohistory option, we physically
1254          * delete the record.
1255          */
1256         if (error == 0 && (hmp->hflags & HMNT_NOHISTORY)) {
1257                 int32_t rec_offset;
1258                 int32_t data_offset;
1259                 int32_t data_len;
1260                 hammer_cluster_t cluster;
1261
1262                 rec_offset = elm->leaf.rec_offset;
1263                 data_offset = elm->leaf.data_offset;
1264                 data_len = elm->leaf.data_len;
1265 #if 0
1266                 kprintf("hammer_ip_delete_record: %08x %08x/%d\n",
1267                         rec_offset, data_offset, data_len);
1268 #endif
1269                 cluster = cursor->node->cluster;
1270                 hammer_ref_cluster(cluster);
1271
1272                 error = hammer_btree_delete(cursor);
1273                 if (error == 0) {
1274                         /*
1275                          * This forces a fixup for the iteration because
1276                          * the cursor is now either sitting at the 'next'
1277                          * element or sitting at the end of a leaf.
1278                          */
1279                         if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
1280                                 cursor->flags |= HAMMER_CURSOR_DELBTREE;
1281                                 cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
1282                         }
1283                         hammer_free_record(cluster, rec_offset);
1284                         if (data_offset && (data_offset - rec_offset < 0 ||
1285                             data_offset - rec_offset >= HAMMER_RECORD_SIZE)) {
1286                                 hammer_free_data(cluster, data_offset,data_len);
1287                         }
1288                 }
1289                 hammer_rel_cluster(cluster, 0);
1290                 if (error) {
1291                         kprintf("hammer_ip_delete_record: unable to physically delete the record!\n");
1292                         error = 0;
1293                 }
1294         }
1295         return(error);
1296 }
1297