diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/ext4_common.c | 21 | ||||
-rw-r--r-- | fs/ext4/ext4_common.h | 5 | ||||
-rw-r--r-- | fs/ext4/ext4_write.c | 12 | ||||
-rw-r--r-- | fs/sandbox/sandboxfs.c | 6 | ||||
-rw-r--r-- | fs/squashfs/sqfs.c | 110 | ||||
-rw-r--r-- | fs/squashfs/sqfs_inode.c | 9 | ||||
-rw-r--r-- | fs/ubifs/replay.c | 2 | ||||
-rw-r--r-- | fs/ubifs/super.c | 9 | ||||
-rw-r--r-- | fs/ubifs/ubifs.c | 40 | ||||
-rw-r--r-- | fs/yaffs2/yaffs_guts.c | 5 |
10 files changed, 166 insertions, 53 deletions
diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 52152a2295a..76f7102456e 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -2181,13 +2181,18 @@ static char *ext4fs_read_symlink(struct ext2fs_node *node) struct ext2fs_node *diro = node; int status; loff_t actread; + size_t alloc_size; if (!diro->inode_read) { status = ext4fs_read_inode(diro->data, diro->ino, &diro->inode); if (status == 0) return NULL; } - symlink = zalloc(le32_to_cpu(diro->inode.size) + 1); + + if (__builtin_add_overflow(le32_to_cpu(diro->inode.size), 1, &alloc_size)) + return NULL; + + symlink = zalloc(alloc_size); if (!symlink) return NULL; @@ -2383,6 +2388,20 @@ int ext4fs_mount(void) fs->inodesz = 128; fs->gdsize = 32; } else { + int missing = __le32_to_cpu(data->sblock.feature_incompat) & + ~(EXT4_FEATURE_INCOMPAT_SUPP | + EXT4_FEATURE_INCOMPAT_SUPP_LAZY_RO); + + if (missing) { + /* + * This code used to be relaxed about feature flags. + * We don't stop the mount to avoid breaking existing setups. + * But, incompatible features can cause serious read errors. + */ + log_err("fs uses incompatible features: %08x, ignoring\n", + missing); + } + debug("EXT4 features COMPAT: %08x INCOMPAT: %08x RO_COMPAT: %08x\n", __le32_to_cpu(data->sblock.feature_compatibility), __le32_to_cpu(data->sblock.feature_incompat), diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h index 84500e990aa..346752092b0 100644 --- a/fs/ext4/ext4_common.h +++ b/fs/ext4/ext4_common.h @@ -24,6 +24,7 @@ #include <ext4fs.h> #include <malloc.h> #include <asm/cache.h> +#include <linux/compat.h> #include <linux/errno.h> #if defined(CONFIG_EXT4_WRITE) #include "ext4_journal.h" @@ -43,9 +44,7 @@ static inline void *zalloc(size_t size) { - void *p = memalign(ARCH_DMA_MINALIGN, size); - memset(p, 0, size); - return p; + return kzalloc(size, 0); } int ext4fs_read_inode(struct ext2_data *data, int ino, diff --git a/fs/ext4/ext4_write.c b/fs/ext4/ext4_write.c index a2dfff81979..d109ed6e90d 100644 --- a/fs/ext4/ext4_write.c +++ b/fs/ext4/ext4_write.c @@ -866,6 +866,7 @@ int ext4fs_write(const char *fname, const char *buffer, ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256); bool store_link_in_inode = false; memset(filename, 0x00, 256); + int missing_feat; if (type != FILETYPE_REG && type != FILETYPE_SYMLINK) return -1; @@ -879,8 +880,15 @@ int ext4fs_write(const char *fname, const char *buffer, return -1; } - if (le32_to_cpu(fs->sb->feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { - printf("Unsupported feature metadata_csum found, not writing.\n"); + missing_feat = le32_to_cpu(fs->sb->feature_incompat) & ~EXT4_FEATURE_INCOMPAT_SUPP; + if (missing_feat) { + log_err("Unsupported features found %08x, not writing.\n", missing_feat); + return -1; + } + + missing_feat = le32_to_cpu(fs->sb->feature_ro_compat) & ~EXT4_FEATURE_RO_COMPAT_SUPP; + if (missing_feat) { + log_err("Unsupported RO compat features found %08x, not writing.\n", missing_feat); return -1; } diff --git a/fs/sandbox/sandboxfs.c b/fs/sandbox/sandboxfs.c index 773b583fa43..76f1a71f412 100644 --- a/fs/sandbox/sandboxfs.c +++ b/fs/sandbox/sandboxfs.c @@ -28,7 +28,7 @@ int sandbox_fs_read_at(const char *filename, loff_t pos, void *buffer, if (fd < 0) return fd; ret = os_lseek(fd, pos, OS_SEEK_SET); - if (ret == -1) { + if (ret < 0) { os_close(fd); return ret; } @@ -65,14 +65,14 @@ int sandbox_fs_write_at(const char *filename, loff_t pos, void *buffer, if (fd < 0) return fd; ret = os_lseek(fd, pos, OS_SEEK_SET); - if (ret == -1) { + if (ret < 0) { os_close(fd); return ret; } size = os_write(fd, buffer, towrite); os_close(fd); - if (size == -1) { + if (size < 0) { ret = -1; } else { ret = 0; diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c index 1430e671a5a..b9314019b1b 100644 --- a/fs/squashfs/sqfs.c +++ b/fs/squashfs/sqfs.c @@ -24,7 +24,12 @@ #include "sqfs_filesystem.h" #include "sqfs_utils.h" +#define MAX_SYMLINK_NEST 8 + static struct squashfs_ctxt ctxt; +static int symlinknest; + +static int sqfs_readdir_nest(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp); static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf) { @@ -422,8 +427,10 @@ static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym, char *resolved, *target; u32 sz; - sz = get_unaligned_le32(&sym->symlink_size); - target = malloc(sz + 1); + if (__builtin_add_overflow(get_unaligned_le32(&sym->symlink_size), 1, &sz)) + return NULL; + + target = malloc(sz); if (!target) return NULL; @@ -431,9 +438,9 @@ static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym, * There is no trailling null byte in the symlink's target path, so a * copy is made and a '\0' is added at its end. */ - target[sz] = '\0'; + target[sz - 1] = '\0'; /* Get target name (relative path) */ - strncpy(target, sym->symlink, sz); + strncpy(target, sym->symlink, sz - 1); /* Relative -> absolute path conversion */ resolved = sqfs_get_abs_path(base_path, target); @@ -472,6 +479,8 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list, /* Start by root inode */ table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes), sblk->inodes, sblk->block_size); + if (!table) + return -EINVAL; dir = (struct squashfs_dir_inode *)table; ldir = (struct squashfs_ldir_inode *)table; @@ -506,7 +515,7 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list, goto out; } - while (!sqfs_readdir(dirsp, &dent)) { + while (!sqfs_readdir_nest(dirsp, &dent)) { ret = strcmp(dent->name, token_list[j]); if (!ret) break; @@ -527,10 +536,17 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list, /* Get reference to inode in the inode table */ table = sqfs_find_inode(dirs->inode_table, new_inode_number, sblk->inodes, sblk->block_size); + if (!table) + return -EINVAL; dir = (struct squashfs_dir_inode *)table; /* Check for symbolic link and inode type sanity */ if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) { + if (++symlinknest == MAX_SYMLINK_NEST) { + ret = -ELOOP; + goto out; + } + sym = (struct squashfs_symlink_inode *)table; /* Get first j + 1 tokens */ path = sqfs_concat_tokens(token_list, j + 1); @@ -551,8 +567,11 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list, ret = -ENOMEM; goto out; } - /* Concatenate remaining tokens and symlink's target */ - res = malloc(strlen(rem) + strlen(target) + 1); + /* + * Concatenate remaining tokens and symlink's target. + * Allocate enough space for rem, target, '/' and '\0'. + */ + res = malloc(strlen(rem) + strlen(target) + 2); if (!res) { ret = -ENOMEM; goto out; @@ -878,7 +897,7 @@ out: return metablks_count; } -int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp) +static int sqfs_opendir_nest(const char *filename, struct fs_dir_stream **dirsp) { unsigned char *inode_table = NULL, *dir_table = NULL; int j, token_count = 0, ret = 0, metablks_count; @@ -973,8 +992,20 @@ out: return ret; } +int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp) +{ + symlinknest = 0; + return sqfs_opendir_nest(filename, dirsp); +} + int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) { + symlinknest = 0; + return sqfs_readdir_nest(fs_dirs, dentp); +} + +static int sqfs_readdir_nest(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) +{ struct squashfs_super_block *sblk = ctxt.sblk; struct squashfs_dir_stream *dirs; struct squashfs_lreg_inode *lreg; @@ -1023,6 +1054,8 @@ int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset; ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes, sblk->block_size); + if (!ipos) + return -SQFS_STOP_READDIR; base = (struct squashfs_base_inode *)ipos; @@ -1317,8 +1350,8 @@ static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg, return datablk_count; } -int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len, - loff_t *actread) +static int sqfs_read_nest(const char *filename, void *buf, loff_t offset, + loff_t len, loff_t *actread) { char *dir = NULL, *fragment_block, *datablock = NULL; char *fragment = NULL, *file = NULL, *resolved, *data; @@ -1348,11 +1381,11 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len, } /* - * sqfs_opendir will uncompress inode and directory tables, and will + * sqfs_opendir_nest will uncompress inode and directory tables, and will * return a pointer to the directory that contains the requested file. */ sqfs_split_path(&file, &dir, filename); - ret = sqfs_opendir(dir, &dirsp); + ret = sqfs_opendir_nest(dir, &dirsp); if (ret) { goto out; } @@ -1360,7 +1393,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len, dirs = (struct squashfs_dir_stream *)dirsp; /* For now, only regular files are able to be loaded */ - while (!sqfs_readdir(dirsp, &dent)) { + while (!sqfs_readdir_nest(dirsp, &dent)) { ret = strcmp(dent->name, file); if (!ret) break; @@ -1379,6 +1412,10 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len, i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset; ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes, sblk->block_size); + if (!ipos) { + ret = -EINVAL; + goto out; + } base = (struct squashfs_base_inode *)ipos; switch (get_unaligned_le16(&base->inode_type)) { @@ -1409,9 +1446,14 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len, break; case SQFS_SYMLINK_TYPE: case SQFS_LSYMLINK_TYPE: + if (++symlinknest == MAX_SYMLINK_NEST) { + ret = -ELOOP; + goto out; + } + symlink = (struct squashfs_symlink_inode *)ipos; resolved = sqfs_resolve_symlink(symlink, filename); - ret = sqfs_read(resolved, buf, offset, len, actread); + ret = sqfs_read_nest(resolved, buf, offset, len, actread); free(resolved); goto out; case SQFS_BLKDEV_TYPE: @@ -1582,7 +1624,14 @@ out: return ret; } -int sqfs_size(const char *filename, loff_t *size) +int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len, + loff_t *actread) +{ + symlinknest = 0; + return sqfs_read_nest(filename, buf, offset, len, actread); +} + +static int sqfs_size_nest(const char *filename, loff_t *size) { struct squashfs_super_block *sblk = ctxt.sblk; struct squashfs_symlink_inode *symlink; @@ -1598,10 +1647,10 @@ int sqfs_size(const char *filename, loff_t *size) sqfs_split_path(&file, &dir, filename); /* - * sqfs_opendir will uncompress inode and directory tables, and will + * sqfs_opendir_nest will uncompress inode and directory tables, and will * return a pointer to the directory that contains the requested file. */ - ret = sqfs_opendir(dir, &dirsp); + ret = sqfs_opendir_nest(dir, &dirsp); if (ret) { ret = -EINVAL; goto free_strings; @@ -1609,7 +1658,7 @@ int sqfs_size(const char *filename, loff_t *size) dirs = (struct squashfs_dir_stream *)dirsp; - while (!sqfs_readdir(dirsp, &dent)) { + while (!sqfs_readdir_nest(dirsp, &dent)) { ret = strcmp(dent->name, file); if (!ret) break; @@ -1627,6 +1676,13 @@ int sqfs_size(const char *filename, loff_t *size) i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset; ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes, sblk->block_size); + + if (!ipos) { + *size = 0; + ret = -EINVAL; + goto free_strings; + } + free(dirs->entry); dirs->entry = NULL; @@ -1642,6 +1698,11 @@ int sqfs_size(const char *filename, loff_t *size) break; case SQFS_SYMLINK_TYPE: case SQFS_LSYMLINK_TYPE: + if (++symlinknest == MAX_SYMLINK_NEST) { + *size = 0; + return -ELOOP; + } + symlink = (struct squashfs_symlink_inode *)ipos; resolved = sqfs_resolve_symlink(symlink, filename); ret = sqfs_size(resolved, size); @@ -1681,10 +1742,11 @@ int sqfs_exists(const char *filename) sqfs_split_path(&file, &dir, filename); /* - * sqfs_opendir will uncompress inode and directory tables, and will + * sqfs_opendir_nest will uncompress inode and directory tables, and will * return a pointer to the directory that contains the requested file. */ - ret = sqfs_opendir(dir, &dirsp); + symlinknest = 0; + ret = sqfs_opendir_nest(dir, &dirsp); if (ret) { ret = -EINVAL; goto free_strings; @@ -1692,7 +1754,7 @@ int sqfs_exists(const char *filename) dirs = (struct squashfs_dir_stream *)dirsp; - while (!sqfs_readdir(dirsp, &dent)) { + while (!sqfs_readdir_nest(dirsp, &dent)) { ret = strcmp(dent->name, file); if (!ret) break; @@ -1709,6 +1771,12 @@ free_strings: return ret == 0; } +int sqfs_size(const char *filename, loff_t *size) +{ + symlinknest = 0; + return sqfs_size_nest(filename, size); +} + void sqfs_close(void) { sqfs_decompressor_cleanup(&ctxt); diff --git a/fs/squashfs/sqfs_inode.c b/fs/squashfs/sqfs_inode.c index d25cfb53e75..bb3ccd37e33 100644 --- a/fs/squashfs/sqfs_inode.c +++ b/fs/squashfs/sqfs_inode.c @@ -78,11 +78,16 @@ int sqfs_inode_size(struct squashfs_base_inode *inode, u32 blk_size) case SQFS_SYMLINK_TYPE: case SQFS_LSYMLINK_TYPE: { + int size; + struct squashfs_symlink_inode *symlink = (struct squashfs_symlink_inode *)inode; - return sizeof(*symlink) + - get_unaligned_le32(&symlink->symlink_size); + if (__builtin_add_overflow(sizeof(*symlink), + get_unaligned_le32(&symlink->symlink_size), &size)) + return -EINVAL; + + return size; } case SQFS_BLKDEV_TYPE: diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index aa7f281ef6b..b6e03b76d41 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -451,7 +451,7 @@ int ubifs_validate_entry(struct ubifs_info *c, if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 || dent->type >= UBIFS_ITYPES_CNT || nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 || - strnlen(dent->name, nlen) != nlen || + (key_type == UBIFS_XENT_KEY && strnlen(dent->name, nlen) != nlen) || le64_to_cpu(dent->inum) > MAX_INUM) { ubifs_err(c, "bad %s node", key_type == UBIFS_DENT_KEY ? "directory entry" : "extended attribute entry"); diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index d8d78a2d3d6..7718081f093 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1758,11 +1758,13 @@ void ubifs_umount(struct ubifs_info *c) ubifs_debugging_exit(c); #ifdef __UBOOT__ ubi_close_volume(c->ubi); + c->ubi = NULL; mutex_unlock(&c->umount_mutex); /* Finally free U-Boot's global copy of superblock */ if (ubifs_sb != NULL) { - free(ubifs_sb->s_fs_info); - free(ubifs_sb); + kfree(ubifs_sb->s_fs_info); + kfree(ubifs_sb); + ubifs_sb = NULL; } #endif } @@ -2061,6 +2063,7 @@ static void ubifs_put_super(struct super_block *sb) #ifndef __UBOOT__ bdi_destroy(&c->bdi); ubi_close_volume(c->ubi); + c->ubi = NULL; mutex_unlock(&c->umount_mutex); #endif } @@ -2321,6 +2324,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) goto out_umount; } #else + ubifs_iput(root); sb->s_root = NULL; #endif @@ -2340,6 +2344,7 @@ out_bdi: out_close: #endif ubi_close_volume(c->ubi); + c->ubi = NULL; out: return err; } diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c index 048730db7ff..398b076d783 100644 --- a/fs/ubifs/ubifs.c +++ b/fs/ubifs/ubifs.c @@ -319,9 +319,7 @@ static int filldir(struct ubifs_info *c, const char *name, int namlen, } ctime_r((time_t *)&inode->i_mtime, filetime); printf("%9lld %24.24s ", inode->i_size, filetime); -#ifndef __UBOOT__ ubifs_iput(inode); -#endif printf("%s\n", name); @@ -557,6 +555,7 @@ static unsigned long ubifs_findfile(struct super_block *sb, char *filename) /* We have some sort of symlink recursion, bail out */ if (symlink_count++ > 8) { + ubifs_iput(inode); printf("Symlink recursion, aborting\n"); return 0; } @@ -568,6 +567,7 @@ static unsigned long ubifs_findfile(struct super_block *sb, char *filename) * the leading slash */ next = name = link_name + 1; root_inum = 1; + ubifs_iput(inode); continue; } /* Relative to cur dir */ @@ -575,6 +575,7 @@ static unsigned long ubifs_findfile(struct super_block *sb, char *filename) link_name, next == NULL ? "" : next); memcpy(symlinkpath, buf, sizeof(buf)); next = name = symlinkpath; + ubifs_iput(inode); continue; } @@ -583,8 +584,10 @@ static unsigned long ubifs_findfile(struct super_block *sb, char *filename) */ /* Found the node! */ - if (!next || *next == '\0') + if (!next || *next == '\0') { + ubifs_iput(inode); return inum; + } root_inum = inum; name = next; @@ -614,7 +617,6 @@ int ubifs_set_blk_dev(struct blk_desc *rbdd, struct disk_partition *info) int ubifs_ls(const char *filename) { - struct ubifs_info *c = ubifs_sb->s_fs_info; struct file *file; struct dentry *dentry; struct inode *dir; @@ -622,7 +624,11 @@ int ubifs_ls(const char *filename) unsigned long inum; int ret = 0; - c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY); + if (!ubifs_is_mounted()) { + debug("UBIFS not mounted, use ubifsmount to mount volume first!\n"); + return -1; + } + inum = ubifs_findfile(ubifs_sb, (char *)filename); if (!inum) { ret = -1; @@ -656,30 +662,33 @@ out_mem: free(dir); out: - ubi_close_volume(c->ubi); return ret; } int ubifs_exists(const char *filename) { - struct ubifs_info *c = ubifs_sb->s_fs_info; unsigned long inum; - c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY); + if (!ubifs_is_mounted()) { + debug("UBIFS not mounted, use ubifsmount to mount volume first!\n"); + return -1; + } + inum = ubifs_findfile(ubifs_sb, (char *)filename); - ubi_close_volume(c->ubi); return inum != 0; } int ubifs_size(const char *filename, loff_t *size) { - struct ubifs_info *c = ubifs_sb->s_fs_info; unsigned long inum; struct inode *inode; int err = 0; - c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY); + if (!ubifs_is_mounted()) { + debug("UBIFS not mounted, use ubifsmount to mount volume first!\n"); + return -1; + } inum = ubifs_findfile(ubifs_sb, (char *)filename); if (!inum) { @@ -698,7 +707,6 @@ int ubifs_size(const char *filename, loff_t *size) ubifs_iput(inode); out: - ubi_close_volume(c->ubi); return err; } @@ -885,6 +893,11 @@ int ubifs_read(const char *filename, void *buf, loff_t offset, int count; int last_block_size = 0; + if (!ubifs_is_mounted()) { + debug("UBIFS not mounted, use ubifsmount to mount volume first!\n"); + return -1; + } + *actread = 0; if (offset & (PAGE_SIZE - 1)) { @@ -893,7 +906,6 @@ int ubifs_read(const char *filename, void *buf, loff_t offset, return -1; } - c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY); /* ubifs_findfile will resolve symlinks, so we know that we get * the real file here */ inum = ubifs_findfile(ubifs_sb, (char *)filename); @@ -957,7 +969,6 @@ put_inode: ubifs_iput(inode); out: - ubi_close_volume(c->ubi); return err; } @@ -988,6 +999,5 @@ void uboot_ubifs_umount(void) printf("Unmounting UBIFS volume %s!\n", ((struct ubifs_info *)(ubifs_sb->s_fs_info))->vi.name); ubifs_umount(ubifs_sb->s_fs_info); - ubifs_sb = NULL; } } diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c index e89d02513c1..c20f2f8298f 100644 --- a/fs/yaffs2/yaffs_guts.c +++ b/fs/yaffs2/yaffs_guts.c @@ -4452,13 +4452,12 @@ loff_t yaffs_get_obj_length(struct yaffs_obj *obj) int yaffs_get_obj_link_count(struct yaffs_obj *obj) { int count = 0; - struct list_head *i; if (!obj->unlinked) count++; /* the object itself */ - list_for_each(i, &obj->hard_links) - count++; /* add the hard links; */ + /* add the hard links; */ + count += list_count_nodes(&obj->hard_links); return count; } |