Whamcloud - gitweb
libext2fs: check for fallocate symbol before using it
[tools/e2fsprogs.git] / lib / ext2fs / ext_attr.c
index 3a281e8..1889824 100644 (file)
@@ -1,14 +1,17 @@
 /*
  * ext_attr.c --- extended attribute blocks
- * 
- * Copyright (C) Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * Copyright (C) 2002 Theodore Ts'o.
  *
  * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
  * %End-Header%
  */
 
+#include "config.h"
 #include <stdio.h>
 #if HAVE_UNISTD_H
 #include <unistd.h>
 
 #include "ext2fs.h"
 
-#ifdef EXT2FS_ENABLE_SWAPFS
-void ext2fs_swap_ext_attr(ext2_filsys fs, char *to, char *from)
+#define NAME_HASH_SHIFT 5
+#define VALUE_HASH_SHIFT 16
+
+/*
+ * ext2_xattr_hash_entry()
+ *
+ * Compute the hash of an extended attribute.
+ */
+__u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data)
 {
-       struct ext2_ext_attr_header *from_header =
-               (struct ext2_ext_attr_header *)from;
-       struct ext2_ext_attr_header *to_header =
-               (struct ext2_ext_attr_header *)to;
-       struct ext2_ext_attr_entry *from_entry, *to_entry;
-       char *from_end = (char *)from_header + fs->blocksize;
+       __u32 hash = 0;
+       char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry);
        int n;
 
-       if (to_header != from_header)
-               memcpy(to_header, from_header, fs->blocksize);
-
-       to_header->h_magic    = ext2fs_swab32(from_header->h_magic);
-       to_header->h_blocks   = ext2fs_swab32(from_header->h_blocks);
-       to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
-       for (n=0; n<4; n++)
-               to_header->h_reserved[n] =
-                       ext2fs_swab32(from_header->h_reserved[n]);
-       
-       from_entry = (struct ext2_ext_attr_entry *)(from_header+1);
-       to_entry   = (struct ext2_ext_attr_entry *)(to_header+1);
-       while ((char *)from_entry < from_end && *(__u32 *)from_entry) {
-               to_entry->e_value_offs  =       
-                       ext2fs_swab16(from_entry->e_value_offs);
-               to_entry->e_value_block =       
-                       ext2fs_swab32(from_entry->e_value_block);
-               to_entry->e_value_size  =       
-                       ext2fs_swab32(from_entry->e_value_size);
-               from_entry = EXT2_EXT_ATTR_NEXT(from_entry);
-               to_entry   = EXT2_EXT_ATTR_NEXT(to_entry);
+       for (n = 0; n < entry->e_name_len; n++) {
+               hash = (hash << NAME_HASH_SHIFT) ^
+                      (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
+                      *name++;
        }
+
+       /* The hash needs to be calculated on the data in little-endian. */
+       if (entry->e_value_block == 0 && entry->e_value_size != 0) {
+               __u32 *value = (__u32 *)data;
+               for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >>
+                        EXT2_EXT_ATTR_PAD_BITS; n; n--) {
+                       hash = (hash << VALUE_HASH_SHIFT) ^
+                              (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
+                              ext2fs_le32_to_cpu(*value++);
+               }
+       }
+
+       return hash;
 }
+
+#undef NAME_HASH_SHIFT
+#undef VALUE_HASH_SHIFT
+
+errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
+{
+       errcode_t       retval;
+
+       retval = io_channel_read_blk64(fs->io, block, 1, buf);
+       if (retval)
+               return retval;
+#ifdef WORDS_BIGENDIAN
+       ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
 #endif
+       return 0;
+}
 
 errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
 {
+       return ext2fs_read_ext_attr2(fs, block, buf);
+}
+
+errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
+{
        errcode_t       retval;
-       struct ext2_ext_attr_entry *entry;
+       char            *write_buf;
+#ifdef WORDS_BIGENDIAN
+       char            *buf = NULL;
 
-       retval = io_channel_read_blk(fs->io, block, 1, buf);
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
        if (retval)
                return retval;
-#ifdef EXT2FS_ENABLE_SWAPFS
-       if ((fs->flags & (EXT2_FLAG_SWAP_BYTES|
-                         EXT2_FLAG_SWAP_BYTES_READ)) != 0)
-               ext2fs_swap_ext_attr(fs, buf, buf);
+       write_buf = buf;
+       ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
+#else
+       write_buf = (char *) inbuf;
 #endif
-       return 0;
+       retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
+#ifdef WORDS_BIGENDIAN
+       ext2fs_free_mem(&buf);
+#endif
+       if (!retval)
+               ext2fs_mark_changed(fs);
+       return retval;
 }
 
 errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
 {
+       return ext2fs_write_ext_attr2(fs, block, inbuf);
+}
+
+/*
+ * This function adjusts the reference count of the EA block.
+ */
+errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
+                                   char *block_buf, int adjust,
+                                   __u32 *newcount)
+{
        errcode_t       retval;
-       char            *p, *end, *write_buf;
-       char            *buf = NULL;
-       struct ext2_dir_entry *dirent;
+       struct ext2_ext_attr_header *header;
+       char    *buf = 0;
 
-#ifdef EXT2FS_ENABLE_SWAPFS
-       if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
-           (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) {
-               retval = ext2fs_get_mem(fs->blocksize, (void **) &buf);
+       if ((blk >= ext2fs_blocks_count(fs->super)) ||
+           (blk < fs->super->s_first_data_block))
+               return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+       if (!block_buf) {
+               retval = ext2fs_get_mem(fs->blocksize, &buf);
                if (retval)
                        return retval;
-               write_buf = buf;
-               ext2fs_swap_ext_attr(fs, buf, inbuf);
-       } else
-#endif
-               write_buf = (char *) inbuf;
-       retval = io_channel_write_blk(fs->io, block, 1, write_buf);
+               block_buf = buf;
+       }
+
+       retval = ext2fs_read_ext_attr2(fs, blk, block_buf);
+       if (retval)
+               goto errout;
+
+       header = (struct ext2_ext_attr_header *) block_buf;
+       header->h_refcount += adjust;
+       if (newcount)
+               *newcount = header->h_refcount;
+
+       retval = ext2fs_write_ext_attr2(fs, blk, block_buf);
+       if (retval)
+               goto errout;
+
+errout:
        if (buf)
-               ext2fs_free_mem((void **) &buf);
-       if (!retval)
-               ext2fs_mark_changed(fs);
+               ext2fs_free_mem(&buf);
        return retval;
 }
+
+errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+                                       char *block_buf, int adjust,
+                                       __u32 *newcount)
+{
+       return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount);
+}