Whamcloud - gitweb
libext2fs: verify and calculate extended attribute block checksums
[tools/e2fsprogs.git] / lib / ext2fs / ext_attr.c
1 /*
2  * ext_attr.c --- extended attribute blocks
3  *
4  * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
5  *
6  * Copyright (C) 2002 Theodore Ts'o.
7  *
8  * %Begin-Header%
9  * This file may be redistributed under the terms of the GNU Library
10  * General Public License, version 2.
11  * %End-Header%
12  */
13
14 #include "config.h"
15 #include <stdio.h>
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #include <string.h>
20 #include <time.h>
21
22 #include "ext2_fs.h"
23 #include "ext2_ext_attr.h"
24
25 #include "ext2fs.h"
26
27 #define NAME_HASH_SHIFT 5
28 #define VALUE_HASH_SHIFT 16
29
30 /*
31  * ext2_xattr_hash_entry()
32  *
33  * Compute the hash of an extended attribute.
34  */
35 __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
36 {
37         __u32 hash = 0;
38         char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry);
39         int n;
40
41         for (n = 0; n < entry->e_name_len; n++) {
42                 hash = (hash << NAME_HASH_SHIFT) ^
43                        (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
44                        *name++;
45         }
46
47         /* The hash needs to be calculated on the data in little-endian. */
48         if (entry->e_value_block == 0 && entry->e_value_size != 0) {
49                 __u32 *value = (__u32 *)data;
50                 for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
51                          EXT2_EXT_ATTR_PAD_BITS; n; n--) {
52                         hash = (hash << VALUE_HASH_SHIFT) ^
53                                (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
54                                ext2fs_le32_to_cpu(*value++);
55                 }
56         }
57
58         return hash;
59 }
60
61 #undef NAME_HASH_SHIFT
62 #undef VALUE_HASH_SHIFT
63
64 errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf,
65                                 ext2_ino_t inum)
66 {
67         errcode_t       retval;
68
69         retval = io_channel_read_blk64(fs->io, block, 1, buf);
70         if (retval)
71                 return retval;
72
73         if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
74             !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf))
75                 retval = EXT2_ET_EXT_ATTR_CSUM_INVALID;
76
77 #ifdef WORDS_BIGENDIAN
78         ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
79 #endif
80
81         return retval;
82 }
83
84 errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
85 {
86         return ext2fs_read_ext_attr3(fs, block, buf, 0);
87 }
88
89 errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
90 {
91         return ext2fs_read_ext_attr2(fs, block, buf);
92 }
93
94 errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, void *inbuf,
95                                  ext2_ino_t inum)
96 {
97         errcode_t       retval;
98         char            *write_buf;
99
100 #ifdef WORDS_BIGENDIAN
101         retval = ext2fs_get_mem(fs->blocksize, &write_buf);
102         if (retval)
103                 return retval;
104         ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1);
105 #else
106         write_buf = (char *) inbuf;
107 #endif
108
109         retval = ext2fs_ext_attr_block_csum_set(fs, inum, block,
110                         (struct ext2_ext_attr_header *)write_buf);
111         if (retval)
112                 return retval;
113
114         retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
115 #ifdef WORDS_BIGENDIAN
116         ext2fs_free_mem(&write_buf);
117 #endif
118         if (!retval)
119                 ext2fs_mark_changed(fs);
120         return retval;
121 }
122
123 errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
124 {
125         return ext2fs_write_ext_attr3(fs, block, inbuf, 0);
126 }
127
128 errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
129 {
130         return ext2fs_write_ext_attr2(fs, block, inbuf);
131 }
132
133 /*
134  * This function adjusts the reference count of the EA block.
135  */
136 errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
137                                     char *block_buf, int adjust,
138                                     __u32 *newcount, ext2_ino_t inum)
139 {
140         errcode_t       retval;
141         struct ext2_ext_attr_header *header;
142         char    *buf = 0;
143
144         if ((blk >= ext2fs_blocks_count(fs->super)) ||
145             (blk < fs->super->s_first_data_block))
146                 return EXT2_ET_BAD_EA_BLOCK_NUM;
147
148         if (!block_buf) {
149                 retval = ext2fs_get_mem(fs->blocksize, &buf);
150                 if (retval)
151                         return retval;
152                 block_buf = buf;
153         }
154
155         retval = ext2fs_read_ext_attr3(fs, blk, block_buf, inum);
156         if (retval)
157                 goto errout;
158
159         header = (struct ext2_ext_attr_header *) block_buf;
160         header->h_refcount += adjust;
161         if (newcount)
162                 *newcount = header->h_refcount;
163
164         retval = ext2fs_write_ext_attr3(fs, blk, block_buf, inum);
165         if (retval)
166                 goto errout;
167
168 errout:
169         if (buf)
170                 ext2fs_free_mem(&buf);
171         return retval;
172 }
173
174 errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
175                                     char *block_buf, int adjust,
176                                     __u32 *newcount)
177 {
178         return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust,
179                                           newcount, 0);
180 }
181
182 errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
183                                         char *block_buf, int adjust,
184                                         __u32 *newcount)
185 {
186         return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
187                                           newcount);
188 }