// SPDX-License-Identifier: GPL-2.0+/* * NILFS directory entry operations * * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. * * Modified for NILFS by Amagai Yoshiji. *//* * linux/fs/ext2/dir.c * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/fs/minix/dir.c * * Copyright (C) 1991, 1992 Linus Torvalds * * ext2 directory handling functions * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 * * All code that works with directory layout had been switched to pagecache * and moved here. AV */ #include <linux/pagemap.h>#include <linux/filelock.h>#include "nilfs.h"#include "page.h" static inline unsigned int nilfs_rec_len_from_disk(__le16 dlen){ unsigned int len = le16_to_cpu(dlen); #if (PAGE_SIZE >= 65536) if (len == NILFS_MAX_REC_LEN) return 1 << 16;#endif return len;} static inline __le16 nilfs_rec_len_to_disk(unsigned int len){#if (PAGE_SIZE >= 65536) if (len == (1 << 16)) return cpu_to_le16(NILFS_MAX_REC_LEN); BUG_ON(len > (1 << 16));#endif return cpu_to_le16(len);} /* * nilfs uses block-sized chunks. Arguably, sector-sized ones would be * more robust, but we have what we have */static inline unsigned int nilfs_chunk_size(struct inode *inode){ return inode->i_sb->s_blocksize;} /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. */static unsigned int nilfs_last_byte(struct inode *inode, unsigned long page_nr){ u64 last_byte = inode->i_size; last_byte -= page_nr << PAGE_SHIFT; if (last_byte > PAGE_SIZE) last_byte = PAGE_SIZE; return last_byte;} static int nilfs_prepare_chunk(struct folio *folio, unsigned int from, unsigned int to){ loff_t pos = folio_pos(folio) + from; return __block_write_begin(folio, pos, to - from, nilfs_get_block);} static void nilfs_commit_chunk(struct folio *folio, struct address_space *mapping, size_t from, size_t to){ struct inode *dir = mapping->host; loff_t pos = folio_pos(folio) + from; size_t copied, len = to - from; unsigned int nr_dirty; int err; nr_dirty = nilfs_page_count_clean_buffers(folio, from, to); copied = block_write_end(pos, len, len, folio); if (pos + copied > dir->i_size) i_size_write(dir, pos + copied); if (IS_DIRSYNC(dir)) nilfs_set_transaction_flag(NILFS_TI_SYNC); err = nilfs_set_file_dirty(dir, nr_dirty); WARN_ON(err); /* do not happen */ folio_unlock(folio);} static bool nilfs_check_folio(struct folio *folio, char *kaddr){ struct inode *dir = folio->mapping->host; struct super_block *sb = dir->i_sb; unsigned int chunk_size = nilfs_chunk_size(dir); size_t offs, rec_len; size_t limit = folio_size(folio); struct nilfs_dir_entry *p; char *error; if (dir->i_size < folio_pos(folio) + limit) { limit = dir->i_size - folio_pos(folio); if (limit & (chunk_size - 1)) goto Ebadsize; if (!limit) goto out; } for (offs = 0; offs <= limit - NILFS_DIR_REC_LEN(1); offs += rec_len) { p = (struct nilfs_dir_entry *)(kaddr + offs); rec_len = nilfs_rec_len_from_disk(p->rec_len); if (rec_len < NILFS_DIR_REC_LEN(1)) goto Eshort; if (rec_len & 3) goto Ealign; if (rec_len < NILFS_DIR_REC_LEN(p->name_len)) goto Enamelen; if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) goto Espan; if (unlikely(p->inode && NILFS_PRIVATE_INODE(le64_to_cpu(p->inode)))) goto Einumber; } if (offs != limit) goto Eend;out: folio_set_checked(folio); return true; /* Too bad, we had an error */ Ebadsize: nilfs_error(sb, "size of directory #%llu is not a multiple of chunk size", dir->i_ino); goto fail;Eshort: error = "rec_len is smaller than minimal"; goto bad_entry;Ealign: error = "unaligned directory entry"; goto bad_entry;Enamelen: error = "rec_len is too small for name_len"; goto bad_entry;Espan: error = "directory entry across blocks"; goto bad_entry;Einumber: error = "disallowed inode number";bad_entry: nilfs_error(sb, "bad entry in directory #%llu: %s - offset=%lu, inode=%llu, rec_len=%zd, name_len=%d", dir->i_ino, error, (folio->index << PAGE_SHIFT) + offs, le64_to_cpu(p->inode), rec_len, p->name_len); goto fail;Eend: p = (struct nilfs_dir_entry *)(kaddr + offs); nilfs_error(sb, "entry in directory #%llu spans the page boundary offset=%lu, inode=%llu", dir->i_ino, (folio->index << PAGE_SHIFT) + offs, le64_to_cpu(p->inode));fail: return false;} static void *nilfs_get_folio(struct inode *dir, unsigned long n, struct folio **foliop){ struct address_space *mapping = dir->i_mapping; struct folio *folio = read_mapping_folio(mapping, n, NULL); void *kaddr; if (IS_ERR(folio)) return folio; kaddr = kmap_local_folio(folio, 0); if (unlikely(!folio_test_checked(folio))) { if (!nilfs_check_folio(folio, kaddr)) goto fail; } *foliop = folio; return kaddr; fail: folio_release_kmap(folio, kaddr); return ERR_PTR(-EIO);} /* * NOTE! unlike strncmp, nilfs_match returns 1 for success, 0 for failure. * * len <= NILFS_NAME_LEN and de != NULL are guaranteed by caller. */static intnilfs_match(int len, const unsigned char *name, struct nilfs_dir_entry *de){ if (len != de->name_len) return 0; if (!de->inode) return 0; return !memcmp(name, de->name, len);} /* * p is at least 6 bytes before the end of page */static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p){ return (struct nilfs_dir_entry *)((char *)p + nilfs_rec_len_from_disk(p->rec_len));} static int nilfs_readdir(struct file *file, struct dir_context *ctx){ loff_t pos = ctx->pos; struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; unsigned int offset = pos & ~PAGE_MASK; unsigned long n = pos >> PAGE_SHIFT; unsigned long npages = dir_pages(inode); if (pos > inode->i_size - NILFS_DIR_REC_LEN(1)) return 0; for ( ; n < npages; n++, offset = 0) { char *kaddr, *limit; struct nilfs_dir_entry *de; struct folio *folio; kaddr = nilfs_get_folio(inode, n, &folio); if (IS_ERR(kaddr)) { nilfs_error(sb, "bad page in #%llu", inode->i_ino); ctx->pos += PAGE_SIZE - offset; return -EIO; } de = (struct nilfs_dir_entry *)(kaddr + offset); limit = kaddr + nilfs_last_byte(inode, n) - NILFS_DIR_REC_LEN(1); for ( ; (char *)de <= limit; de = nilfs_next_entry(de)) { if (de->rec_len == 0) { nilfs_error(sb, "zero-length directory entry"); folio_release_kmap(folio, kaddr); return -EIO; } if (de->inode) { unsigned char t; t = fs_ftype_to_dtype(de->file_type); if (!dir_emit(ctx, de->name, de->name_len, le64_to_cpu(de->inode), t)) { folio_release_kmap(folio, kaddr); return 0; } } ctx->pos += nilfs_rec_len_from_disk(de->rec_len); } folio_release_kmap(folio, kaddr); } return 0;} /* * nilfs_find_entry() * * Finds an entry in the specified directory with the wanted name. It * returns the folio in which the entry was found, and the entry itself. * The folio is mapped and unlocked. When the caller is finished with * the entry, it should call folio_release_kmap(). * * On failure, returns an error pointer and the caller should ignore foliop. */struct nilfs_dir_entry *nilfs_find_entry(struct inode *dir, const struct qstr *qstr, struct folio **foliop){ const unsigned char *name = qstr->name; int namelen = qstr->len; unsigned int reclen = NILFS_DIR_REC_LEN(namelen); unsigned long start, n; unsigned long npages = dir_pages(dir); struct nilfs_inode_info *ei = NILFS_I(dir); struct nilfs_dir_entry *de; if (npages == 0) goto out; start = ei->i_dir_start_lookup; if (start >= npages) start = 0; n = start; do { char *kaddr = nilfs_get_folio(dir, n, foliop); if (IS_ERR(kaddr)) return ERR_CAST(kaddr); de = (struct nilfs_dir_entry *)kaddr; kaddr += nilfs_last_byte(dir, n) - reclen; while ((char *)de <= kaddr) { if (de->rec_len == 0) { nilfs_error(dir->i_sb, "zero-length directory entry"); folio_release_kmap(*foliop, kaddr); goto out; } if (nilfs_match(namelen, name, de)) goto found; de = nilfs_next_entry(de); } folio_release_kmap(*foliop, kaddr); if (++n >= npages) n = 0; /* next folio is past the blocks we've got */ if (unlikely(n > (dir->i_blocks >> (PAGE_SHIFT - 9)))) { nilfs_error(dir->i_sb, "dir %llu size %lld exceeds block count %llu", dir->i_ino, dir->i_size, (unsigned long long)dir->i_blocks); goto out; } } while (n != start);out: return ERR_PTR(-ENOENT); found: ei->i_dir_start_lookup = n; return de;} struct nilfs_dir_entry *nilfs_dotdot(struct inode *dir, struct folio **foliop){ struct folio *folio; struct nilfs_dir_entry *de, *next_de; size_t limit; char *msg; de = nilfs_get_folio(dir, 0, &folio); if (IS_ERR(de)) return NULL; limit = nilfs_last_byte(dir, 0); /* is a multiple of chunk size */ if (unlikely(!limit || le64_to_cpu(de->inode) != dir->i_ino || !nilfs_match(1, ".", de))) { msg = "missing '.'"; goto fail; } next_de = nilfs_next_entry(de); /* * If "next_de" has not reached the end of the chunk, there is * at least one more record. Check whether it matches "..". */ if (unlikely((char *)next_de == (char *)de + nilfs_chunk_size(dir) || !nilfs_match(2, "..", next_de))) { msg = "missing '..'"; goto fail; } *foliop = folio; return next_de; fail: nilfs_error(dir->i_sb, "directory #%llu %s", dir->i_ino, msg); folio_release_kmap(folio, de); return NULL;} int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, u64 *ino){ struct nilfs_dir_entry *de; struct folio *folio; de = nilfs_find_entry(dir, qstr, &folio); if (IS_ERR(de)) return PTR_ERR(de); *ino = le64_to_cpu(de->inode); folio_release_kmap(folio, de); return 0;} int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, struct folio *folio, struct inode *inode){ size_t from = offset_in_folio(folio, de); size_t to = from + nilfs_rec_len_from_disk(de->rec_len); struct address_space *mapping = folio->mapping; int err; folio_lock(folio); err = nilfs_prepare_chunk(folio, from, to); if (unlikely(err)) { folio_unlock(folio); return err; } de->inode = cpu_to_le64(inode->i_ino); de->file_type = fs_umode_to_ftype(inode->i_mode); nilfs_commit_chunk(folio, mapping, from, to); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); return 0;} /* * Parent is locked. */int nilfs_add_link(struct dentry *dentry, struct inode *inode){ struct inode *dir = d_inode(dentry->d_parent); const unsigned char *name = dentry->d_name.name; int namelen = dentry->d_name.len; unsigned int chunk_size = nilfs_chunk_size(dir); unsigned int reclen = NILFS_DIR_REC_LEN(namelen); unsigned short rec_len, name_len; struct folio *folio = NULL; struct nilfs_dir_entry *de; unsigned long npages = dir_pages(dir); unsigned long n; size_t from, to; int err; /* * We take care of directory expansion in the same loop. * This code plays outside i_size, so it locks the folio * to protect that region. */ for (n = 0; n <= npages; n++) { char *kaddr = nilfs_get_folio(dir, n, &folio); char *dir_end; if (IS_ERR(kaddr)) return PTR_ERR(kaddr); folio_lock(folio); dir_end = kaddr + nilfs_last_byte(dir, n); de = (struct nilfs_dir_entry *)kaddr; kaddr += folio_size(folio) - reclen; while ((char *)de <= kaddr) { if ((char *)de == dir_end) { /* We hit i_size */ name_len = 0; rec_len = chunk_size; de->rec_len = nilfs_rec_len_to_disk(chunk_size); de->inode = 0; goto got_it; } if (de->rec_len == 0) { nilfs_error(dir->i_sb, "zero-length directory entry"); err = -EIO; goto out_unlock; } err = -EEXIST; if (nilfs_match(namelen, name, de)) goto out_unlock; name_len = NILFS_DIR_REC_LEN(de->name_len); rec_len = nilfs_rec_len_from_disk(de->rec_len); if (!de->inode && rec_len >= reclen) goto got_it; if (rec_len >= name_len + reclen) goto got_it; de = (struct nilfs_dir_entry *)((char *)de + rec_len); } folio_unlock(folio); folio_release_kmap(folio, kaddr); } BUG(); return -EINVAL; got_it: from = offset_in_folio(folio, de); to = from + rec_len; err = nilfs_prepare_chunk(folio, from, to); if (err) goto out_unlock; if (de->inode) { struct nilfs_dir_entry *de1; de1 = (struct nilfs_dir_entry *)((char *)de + name_len); de1->rec_len = nilfs_rec_len_to_disk(rec_len - name_len); de->rec_len = nilfs_rec_len_to_disk(name_len);Lines 1–500 of 665. Load more
// SPDX-License-Identifier: GPL-2.0+ /* * NILFS directory entry operations * * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. * * Modified for NILFS by Amagai Yoshiji. */ /* * linux/fs/ext2/dir.c * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/fs/minix/dir.c * * Copyright (C) 1991, 1992 Linus Torvalds * * ext2 directory handling functions * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 * * All code that works with directory layout had been switched to pagecache * and moved here. AV */ #include <linux/pagemap.h> #include <linux/filelock.h> #include "nilfs.h" #include "page.h" static inline unsigned int nilfs_rec_len_from_disk(__le16 dlen) { unsigned int len = le16_to_cpu(dlen); #if (PAGE_SIZE >= 65536) if (len == NILFS_MAX_REC_LEN) return 1 << 16; #endif return len; } static inline __le16 nilfs_rec_len_to_disk(unsigned int len) { #if (PAGE_SIZE >= 65536) if (len == (1 << 16)) return cpu_to_le16(NILFS_MAX_REC_LEN); BUG_ON(len > (1 << 16)); #endif return cpu_to_le16(len); } /* * nilfs uses block-sized chunks. Arguably, sector-sized ones would be * more robust, but we have what we have */ static inline unsigned int nilfs_chunk_size(struct inode *inode) { return inode->i_sb->s_blocksize; } /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. */ static unsigned int nilfs_last_byte(struct inode *inode, unsigned long page_nr) { u64 last_byte = inode->i_size; last_byte -= page_nr << PAGE_SHIFT; if (last_byte > PAGE_SIZE) last_byte = PAGE_SIZE; return last_byte; } static int nilfs_prepare_chunk(struct folio *folio, unsigned int from, unsigned int to) { loff_t pos = folio_pos(folio) + from; return __block_write_begin(folio, pos, to - from, nilfs_get_block); } static void nilfs_commit_chunk(struct folio *folio, struct address_space *mapping, size_t from, size_t to) { struct inode *dir = mapping->host; loff_t pos = folio_pos(folio) + from; size_t copied, len = to - from; unsigned int nr_dirty; int err; nr_dirty = nilfs_page_count_clean_buffers(folio, from, to); copied = block_write_end(pos, len, len, folio); if (pos + copied > dir->i_size) i_size_write(dir, pos + copied); if (IS_DIRSYNC(dir)) nilfs_set_transaction_flag(NILFS_TI_SYNC); err = nilfs_set_file_dirty(dir, nr_dirty); WARN_ON(err); /* do not happen */ folio_unlock(folio); } static bool nilfs_check_folio(struct folio *folio, char *kaddr) { struct inode *dir = folio->mapping->host; struct super_block *sb = dir->i_sb; unsigned int chunk_size = nilfs_chunk_size(dir); size_t offs, rec_len; size_t limit = folio_size(folio); struct nilfs_dir_entry *p; char *error; if (dir->i_size < folio_pos(folio) + limit) { limit = dir->i_size - folio_pos(folio); if (limit & (chunk_size - 1)) goto Ebadsize; if (!limit) goto out; } for (offs = 0; offs <= limit - NILFS_DIR_REC_LEN(1); offs += rec_len) { p = (struct nilfs_dir_entry *)(kaddr + offs); rec_len = nilfs_rec_len_from_disk(p->rec_len); if (rec_len < NILFS_DIR_REC_LEN(1)) goto Eshort; if (rec_len & 3) goto Ealign; if (rec_len < NILFS_DIR_REC_LEN(p->name_len)) goto Enamelen; if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) goto Espan; if (unlikely(p->inode && NILFS_PRIVATE_INODE(le64_to_cpu(p->inode)))) goto Einumber; } if (offs != limit) goto Eend; out: folio_set_checked(folio); return true; /* Too bad, we had an error */ Ebadsize: nilfs_error(sb, "size of directory #%llu is not a multiple of chunk size", dir->i_ino); goto fail; Eshort: error = "rec_len is smaller than minimal"; goto bad_entry; Ealign: error = "unaligned directory entry"; goto bad_entry; Enamelen: error = "rec_len is too small for name_len"; goto bad_entry; Espan: error = "directory entry across blocks"; goto bad_entry; Einumber: error = "disallowed inode number"; bad_entry: nilfs_error(sb, "bad entry in directory #%llu: %s - offset=%lu, inode=%llu, rec_len=%zd, name_len=%d", dir->i_ino, error, (folio->index << PAGE_SHIFT) + offs, le64_to_cpu(p->inode), rec_len, p->name_len); goto fail; Eend: p = (struct nilfs_dir_entry *)(kaddr + offs); nilfs_error(sb, "entry in directory #%llu spans the page boundary offset=%lu, inode=%llu", dir->i_ino, (folio->index << PAGE_SHIFT) + offs, le64_to_cpu(p->inode)); fail: return false; } static void *nilfs_get_folio(struct inode *dir, unsigned long n, struct folio **foliop) { struct address_space *mapping = dir->i_mapping; struct folio *folio = read_mapping_folio(mapping, n, NULL); void *kaddr; if (IS_ERR(folio)) return folio; kaddr = kmap_local_folio(folio, 0); if (unlikely(!folio_test_checked(folio))) { if (!nilfs_check_folio(folio, kaddr)) goto fail; } *foliop = folio; return kaddr; fail: folio_release_kmap(folio, kaddr); return ERR_PTR(-EIO); } /* * NOTE! unlike strncmp, nilfs_match returns 1 for success, 0 for failure. * * len <= NILFS_NAME_LEN and de != NULL are guaranteed by caller. */ static int nilfs_match(int len, const unsigned char *name, struct nilfs_dir_entry *de) { if (len != de->name_len) return 0; if (!de->inode) return 0; return !memcmp(name, de->name, len); } /* * p is at least 6 bytes before the end of page */ static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p) { return (struct nilfs_dir_entry *)((char *)p + nilfs_rec_len_from_disk(p->rec_len)); } static int nilfs_readdir(struct file *file, struct dir_context *ctx) { loff_t pos = ctx->pos; struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; unsigned int offset = pos & ~PAGE_MASK; unsigned long n = pos >> PAGE_SHIFT; unsigned long npages = dir_pages(inode); if (pos > inode->i_size - NILFS_DIR_REC_LEN(1)) return 0; for ( ; n < npages; n++, offset = 0) { char *kaddr, *limit; struct nilfs_dir_entry *de; struct folio *folio; kaddr = nilfs_get_folio(inode, n, &folio); if (IS_ERR(kaddr)) { nilfs_error(sb, "bad page in #%llu", inode->i_ino); ctx->pos += PAGE_SIZE - offset; return -EIO; } de = (struct nilfs_dir_entry *)(kaddr + offset); limit = kaddr + nilfs_last_byte(inode, n) - NILFS_DIR_REC_LEN(1); for ( ; (char *)de <= limit; de = nilfs_next_entry(de)) { if (de->rec_len == 0) { nilfs_error(sb, "zero-length directory entry"); folio_release_kmap(folio, kaddr); return -EIO; } if (de->inode) { unsigned char t; t = fs_ftype_to_dtype(de->file_type); if (!dir_emit(ctx, de->name, de->name_len, le64_to_cpu(de->inode), t)) { folio_release_kmap(folio, kaddr); return 0; } } ctx->pos += nilfs_rec_len_from_disk(de->rec_len); } folio_release_kmap(folio, kaddr); } return 0; } /* * nilfs_find_entry() * * Finds an entry in the specified directory with the wanted name. It * returns the folio in which the entry was found, and the entry itself. * The folio is mapped and unlocked. When the caller is finished with * the entry, it should call folio_release_kmap(). * * On failure, returns an error pointer and the caller should ignore foliop. */ struct nilfs_dir_entry *nilfs_find_entry(struct inode *dir, const struct qstr *qstr, struct folio **foliop) { const unsigned char *name = qstr->name; int namelen = qstr->len; unsigned int reclen = NILFS_DIR_REC_LEN(namelen); unsigned long start, n; unsigned long npages = dir_pages(dir); struct nilfs_inode_info *ei = NILFS_I(dir); struct nilfs_dir_entry *de; if (npages == 0) goto out; start = ei->i_dir_start_lookup; if (start >= npages) start = 0; n = start; do { char *kaddr = nilfs_get_folio(dir, n, foliop); if (IS_ERR(kaddr)) return ERR_CAST(kaddr); de = (struct nilfs_dir_entry *)kaddr; kaddr += nilfs_last_byte(dir, n) - reclen; while ((char *)de <= kaddr) { if (de->rec_len == 0) { nilfs_error(dir->i_sb, "zero-length directory entry"); folio_release_kmap(*foliop, kaddr); goto out; } if (nilfs_match(namelen, name, de)) goto found; de = nilfs_next_entry(de); } folio_release_kmap(*foliop, kaddr); if (++n >= npages) n = 0; /* next folio is past the blocks we've got */ if (unlikely(n > (dir->i_blocks >> (PAGE_SHIFT - 9)))) { nilfs_error(dir->i_sb, "dir %llu size %lld exceeds block count %llu", dir->i_ino, dir->i_size, (unsigned long long)dir->i_blocks); goto out; } } while (n != start); out: return ERR_PTR(-ENOENT); found: ei->i_dir_start_lookup = n; return de; } struct nilfs_dir_entry *nilfs_dotdot(struct inode *dir, struct folio **foliop) { struct folio *folio; struct nilfs_dir_entry *de, *next_de; size_t limit; char *msg; de = nilfs_get_folio(dir, 0, &folio); if (IS_ERR(de)) return NULL; limit = nilfs_last_byte(dir, 0); /* is a multiple of chunk size */ if (unlikely(!limit || le64_to_cpu(de->inode) != dir->i_ino || !nilfs_match(1, ".", de))) { msg = "missing '.'"; goto fail; } next_de = nilfs_next_entry(de); /* * If "next_de" has not reached the end of the chunk, there is * at least one more record. Check whether it matches "..". */ if (unlikely((char *)next_de == (char *)de + nilfs_chunk_size(dir) || !nilfs_match(2, "..", next_de))) { msg = "missing '..'"; goto fail; } *foliop = folio; return next_de; fail: nilfs_error(dir->i_sb, "directory #%llu %s", dir->i_ino, msg); folio_release_kmap(folio, de); return NULL; } int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, u64 *ino) { struct nilfs_dir_entry *de; struct folio *folio; de = nilfs_find_entry(dir, qstr, &folio); if (IS_ERR(de)) return PTR_ERR(de); *ino = le64_to_cpu(de->inode); folio_release_kmap(folio, de); return 0; } int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, struct folio *folio, struct inode *inode) { size_t from = offset_in_folio(folio, de); size_t to = from + nilfs_rec_len_from_disk(de->rec_len); struct address_space *mapping = folio->mapping; int err; folio_lock(folio); err = nilfs_prepare_chunk(folio, from, to); if (unlikely(err)) { folio_unlock(folio); return err; } de->inode = cpu_to_le64(inode->i_ino); de->file_type = fs_umode_to_ftype(inode->i_mode); nilfs_commit_chunk(folio, mapping, from, to); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); return 0; } /* * Parent is locked. */ int nilfs_add_link(struct dentry *dentry, struct inode *inode) { struct inode *dir = d_inode(dentry->d_parent); const unsigned char *name = dentry->d_name.name; int namelen = dentry->d_name.len; unsigned int chunk_size = nilfs_chunk_size(dir); unsigned int reclen = NILFS_DIR_REC_LEN(namelen); unsigned short rec_len, name_len; struct folio *folio = NULL; struct nilfs_dir_entry *de; unsigned long npages = dir_pages(dir); unsigned long n; size_t from, to; int err; /* * We take care of directory expansion in the same loop. * This code plays outside i_size, so it locks the folio * to protect that region. */ for (n = 0; n <= npages; n++) { char *kaddr = nilfs_get_folio(dir, n, &folio); char *dir_end; if (IS_ERR(kaddr)) return PTR_ERR(kaddr); folio_lock(folio); dir_end = kaddr + nilfs_last_byte(dir, n); de = (struct nilfs_dir_entry *)kaddr; kaddr += folio_size(folio) - reclen; while ((char *)de <= kaddr) { if ((char *)de == dir_end) { /* We hit i_size */ name_len = 0; rec_len = chunk_size; de->rec_len = nilfs_rec_len_to_disk(chunk_size); de->inode = 0; goto got_it; } if (de->rec_len == 0) { nilfs_error(dir->i_sb, "zero-length directory entry"); err = -EIO; goto out_unlock; } err = -EEXIST; if (nilfs_match(namelen, name, de)) goto out_unlock; name_len = NILFS_DIR_REC_LEN(de->name_len); rec_len = nilfs_rec_len_from_disk(de->rec_len); if (!de->inode && rec_len >= reclen) goto got_it; if (rec_len >= name_len + reclen) goto got_it; de = (struct nilfs_dir_entry *)((char *)de + rec_len); } folio_unlock(folio); folio_release_kmap(folio, kaddr); } BUG(); return -EINVAL; got_it: from = offset_in_folio(folio, de); to = from + rec_len; err = nilfs_prepare_chunk(folio, from, to); if (err) goto out_unlock; if (de->inode) { struct nilfs_dir_entry *de1; de1 = (struct nilfs_dir_entry *)((char *)de + name_len); de1->rec_len = nilfs_rec_len_to_disk(rec_len - name_len); de->rec_len = nilfs_rec_len_to_disk(name_len);