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 <stdio.h> 15#if HAVE_UNISTD_H 16#include <unistd.h> 17#endif 18#include <string.h> 19#include <time.h> 20 21#include "ext2_fs.h" 22#include "ext2_ext_attr.h" 23 24#include "ext2fs.h" 25 26#define NAME_HASH_SHIFT 5 27#define VALUE_HASH_SHIFT 16 28 29/* 30 * ext2_xattr_hash_entry() 31 * 32 * Compute the hash of an extended attribute. 33 */ 34__u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data) 35{ 36 __u32 hash = 0; 37 char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry); 38 int n; 39 40 for (n = 0; n < entry->e_name_len; n++) { 41 hash = (hash << NAME_HASH_SHIFT) ^ 42 (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ 43 *name++; 44 } 45 46 /* The hash needs to be calculated on the data in little-endian. */ 47 if (entry->e_value_block == 0 && entry->e_value_size != 0) { 48 __u32 *value = (__u32 *)data; 49 for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >> 50 EXT2_EXT_ATTR_PAD_BITS; n; n--) { 51 hash = (hash << VALUE_HASH_SHIFT) ^ 52 (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ 53 ext2fs_le32_to_cpu(*value++); 54 } 55 } 56 57 return hash; 58} 59 60#undef NAME_HASH_SHIFT 61#undef VALUE_HASH_SHIFT 62 63errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf) 64{ 65 errcode_t retval; 66 67 retval = io_channel_read_blk64(fs->io, block, 1, buf); 68 if (retval) 69 return retval; 70#ifdef WORDS_BIGENDIAN 71 ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); 72#endif 73 return 0; 74} 75 76errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) 77{ 78 return ext2fs_read_ext_attr2(fs, block, buf); 79} 80 81errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf) 82{ 83 errcode_t retval; 84 char *write_buf; 85#ifdef WORDS_BIGENDIAN 86 char *buf = NULL; 87 88 retval = ext2fs_get_mem(fs->blocksize, &buf); 89 if (retval) 90 return retval; 91 write_buf = buf; 92 ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); 93#else 94 write_buf = (char *) inbuf; 95#endif 96 retval = io_channel_write_blk64(fs->io, block, 1, write_buf); 97#ifdef WORDS_BIGENDIAN 98 ext2fs_free_mem(&buf); 99#endif 100 if (!retval) 101 ext2fs_mark_changed(fs); 102 return retval; 103} 104 105errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) 106{ 107 return ext2fs_write_ext_attr2(fs, block, inbuf); 108} 109 110/* 111 * This function adjusts the reference count of the EA block. 112 */ 113errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, 114 char *block_buf, int adjust, 115 __u32 *newcount) 116{ 117 errcode_t retval; 118 struct ext2_ext_attr_header *header; 119 char *buf = 0; 120 121 if ((blk >= ext2fs_blocks_count(fs->super)) || 122 (blk < fs->super->s_first_data_block)) 123 return EXT2_ET_BAD_EA_BLOCK_NUM; 124 125 if (!block_buf) { 126 retval = ext2fs_get_mem(fs->blocksize, &buf); 127 if (retval) 128 return retval; 129 block_buf = buf; 130 } 131 132 retval = ext2fs_read_ext_attr2(fs, blk, block_buf); 133 if (retval) 134 goto errout; 135 136 header = (struct ext2_ext_attr_header *) block_buf; 137 header->h_refcount += adjust; 138 if (newcount) 139 *newcount = header->h_refcount; 140 141 retval = ext2fs_write_ext_attr2(fs, blk, block_buf); 142 if (retval) 143 goto errout; 144 145errout: 146 if (buf) 147 ext2fs_free_mem(&buf); 148 return retval; 149} 150 151errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, 152 char *block_buf, int adjust, 153 __u32 *newcount) 154{ 155 return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount); 156} 157