📄 namei.c
字号:
const int rlen = le16_to_cpu(de->rec_len); if (rlen < OCFS2_DIR_REC_LEN(1)) error_msg = "rec_len is smaller than minimal"; else if (rlen % 4 != 0) error_msg = "rec_len % 4 != 0"; else if (rlen < OCFS2_DIR_REC_LEN(de->name_len)) error_msg = "rec_len is too small for name_len"; else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize) error_msg = "directory entry across blocks"; if (error_msg != NULL) mlog(ML_ERROR, "bad entry in directory #%"MLFu64": %s - " "offset=%lu, inode=%"MLFu64", rec_len=%d, name_len=%d\n", OCFS2_I(dir)->ip_blkno, error_msg, offset, le64_to_cpu(de->inode), rlen, de->name_len); return error_msg == NULL ? 1 : 0;}/* we don't always have a dentry for what we want to add, so people * like orphan dir can call this instead. * * If you pass me insert_bh, I'll skip the search of the other dir * blocks and put the record in there. */static int __ocfs2_add_entry(struct ocfs2_journal_handle *handle, struct inode *dir, const char *name, int namelen, struct inode *inode, u64 blkno, struct buffer_head *parent_fe_bh, struct buffer_head *insert_bh){ unsigned long offset; unsigned short rec_len; struct ocfs2_dir_entry *de, *de1; struct super_block *sb; int retval, status; mlog_entry_void(); sb = dir->i_sb; if (!namelen) return -EINVAL; rec_len = OCFS2_DIR_REC_LEN(namelen); offset = 0; de = (struct ocfs2_dir_entry *) insert_bh->b_data; while (1) { BUG_ON((char *)de >= sb->s_blocksize + insert_bh->b_data); /* These checks should've already been passed by the * prepare function, but I guess we can leave them * here anyway. */ if (!ocfs2_check_dir_entry(dir, de, insert_bh, offset)) { retval = -ENOENT; goto bail; } if (ocfs2_match(namelen, name, de)) { retval = -EEXIST; goto bail; } if (((le64_to_cpu(de->inode) == 0) && (le16_to_cpu(de->rec_len) >= rec_len)) || (le16_to_cpu(de->rec_len) >= (OCFS2_DIR_REC_LEN(de->name_len) + rec_len))) { dir->i_mtime = dir->i_ctime = CURRENT_TIME; retval = ocfs2_mark_inode_dirty(handle, dir, parent_fe_bh); if (retval < 0) { mlog_errno(retval); goto bail; } status = ocfs2_journal_access(handle, dir, insert_bh, OCFS2_JOURNAL_ACCESS_WRITE); /* By now the buffer is marked for journaling */ offset += le16_to_cpu(de->rec_len); if (le64_to_cpu(de->inode)) { de1 = (struct ocfs2_dir_entry *)((char *) de + OCFS2_DIR_REC_LEN(de->name_len)); de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) - OCFS2_DIR_REC_LEN(de->name_len)); de->rec_len = cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len)); de = de1; } de->file_type = OCFS2_FT_UNKNOWN; if (blkno) { de->inode = cpu_to_le64(blkno); ocfs2_set_de_type(de, inode->i_mode); } else de->inode = 0; de->name_len = namelen; memcpy(de->name, name, namelen); dir->i_version++; status = ocfs2_journal_dirty(handle, insert_bh); retval = 0; goto bail; } offset += le16_to_cpu(de->rec_len); de = (struct ocfs2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); } /* when you think about it, the assert above should prevent us * from ever getting here. */ retval = -ENOSPC;bail: mlog_exit(retval); return retval;}/* * ocfs2_delete_entry deletes a directory entry by merging it with the * previous entry */static int ocfs2_delete_entry(struct ocfs2_journal_handle *handle, struct inode *dir, struct ocfs2_dir_entry *de_del, struct buffer_head *bh){ struct ocfs2_dir_entry *de, *pde; int i, status = -ENOENT; mlog_entry("(0x%p, 0x%p, 0x%p, 0x%p)\n", handle, dir, de_del, bh); i = 0; pde = NULL; de = (struct ocfs2_dir_entry *) bh->b_data; while (i < bh->b_size) { if (!ocfs2_check_dir_entry(dir, de, bh, i)) { status = -EIO; mlog_errno(status); goto bail; } if (de == de_del) { status = ocfs2_journal_access(handle, dir, bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { status = -EIO; mlog_errno(status); goto bail; } if (pde) pde->rec_len = cpu_to_le16(le16_to_cpu(pde->rec_len) + le16_to_cpu(de->rec_len)); else de->inode = 0; dir->i_version++; status = ocfs2_journal_dirty(handle, bh); goto bail; } i += le16_to_cpu(de->rec_len); pde = de; de = (struct ocfs2_dir_entry *)((char *)de + le16_to_cpu(de->rec_len)); }bail: mlog_exit(status); return status;}/* * Returns 0 if not found, -1 on failure, and 1 on success */static int inline ocfs2_search_dirblock(struct buffer_head *bh, struct inode *dir, const char *name, int namelen, unsigned long offset, struct ocfs2_dir_entry **res_dir){ struct ocfs2_dir_entry *de; char *dlimit, *de_buf; int de_len; int ret = 0; mlog_entry_void(); de_buf = bh->b_data; dlimit = de_buf + dir->i_sb->s_blocksize; while (de_buf < dlimit) { /* this code is executed quadratically often */ /* do minimal checking `by hand' */ de = (struct ocfs2_dir_entry *) de_buf; if (de_buf + namelen <= dlimit && ocfs2_match(namelen, name, de)) { /* found a match - just to be sure, do a full check */ if (!ocfs2_check_dir_entry(dir, de, bh, offset)) { ret = -1; goto bail; } *res_dir = de; ret = 1; goto bail; } /* prevent looping on a bad block */ de_len = le16_to_cpu(de->rec_len); if (de_len <= 0) { ret = -1; goto bail; } de_buf += de_len; offset += de_len; }bail: mlog_exit(ret); return ret;}struct buffer_head *ocfs2_find_entry(const char *name, int namelen, struct inode *dir, struct ocfs2_dir_entry **res_dir){ struct super_block *sb; struct buffer_head *bh_use[NAMEI_RA_SIZE]; struct buffer_head *bh, *ret = NULL; unsigned long start, block, b; int ra_max = 0; /* Number of bh's in the readahead buffer, bh_use[] */ int ra_ptr = 0; /* Current index into readahead buffer */ int num = 0; int nblocks, i, err; mlog_entry_void(); *res_dir = NULL; sb = dir->i_sb; nblocks = i_size_read(dir) >> sb->s_blocksize_bits; start = OCFS2_I(dir)->ip_dir_start_lookup; if (start >= nblocks) start = 0; block = start;restart: do { /* * We deal with the read-ahead logic here. */ if (ra_ptr >= ra_max) { /* Refill the readahead buffer */ ra_ptr = 0; b = block; for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) { /* * Terminate if we reach the end of the * directory and must wrap, or if our * search has finished at this block. */ if (b >= nblocks || (num && block == start)) { bh_use[ra_max] = NULL; break; } num++; /* XXX: questionable readahead stuff here */ bh = ocfs2_bread(dir, b++, &err, 1); bh_use[ra_max] = bh;#if 0 // ??? if (bh) ll_rw_block(READ, 1, &bh);#endif } } if ((bh = bh_use[ra_ptr++]) == NULL) goto next; wait_on_buffer(bh); if (!buffer_uptodate(bh)) { /* read error, skip block & hope for the best */ brelse(bh); goto next; } i = ocfs2_search_dirblock(bh, dir, name, namelen, block << sb->s_blocksize_bits, res_dir); if (i == 1) { OCFS2_I(dir)->ip_dir_start_lookup = block; ret = bh; goto cleanup_and_exit; } else { brelse(bh); if (i < 0) goto cleanup_and_exit; } next: if (++block >= nblocks) block = 0; } while (block != start); /* * If the directory has grown while we were searching, then * search the last part of the directory before giving up. */ block = nblocks; nblocks = i_size_read(dir) >> sb->s_blocksize_bits; if (block < nblocks) { start = 0; goto restart; }cleanup_and_exit: /* Clean up the read-ahead blocks */ for (; ra_ptr < ra_max; ra_ptr++) brelse(bh_use[ra_ptr]); mlog_exit_ptr(ret); return ret;}static int ocfs2_blkno_stringify(u64 blkno, char *name){ int status, namelen; mlog_entry_void(); namelen = snprintf(name, OCFS2_ORPHAN_NAMELEN + 1, "%016"MLFx64, blkno); if (namelen <= 0) { if (namelen) status = namelen; else status = -EINVAL; mlog_errno(status); goto bail; } if (namelen != OCFS2_ORPHAN_NAMELEN) { status = -EINVAL; mlog_errno(status); goto bail; } mlog(0, "built filename '%s' for orphan dir (len=%d)\n", name, namelen); status = 0;bail: mlog_exit(status); return status;}static int ocfs2_prepare_orphan_dir(struct ocfs2_super *osb, struct ocfs2_journal_handle *handle, struct inode *inode, char *name, struct buffer_head **de_bh){ struct inode *orphan_dir_inode = NULL; struct buffer_head *orphan_dir_bh = NULL; int status = 0; status = ocfs2_blkno_stringify(OCFS2_I(inode)->ip_blkno, name); if (status < 0) { mlog_errno(status); goto leave; } orphan_dir_inode = ocfs2_get_system_file_inode(osb, ORPHAN_DIR_SYSTEM_INODE, osb->slot_num); if (!orphan_dir_inode) { status = -ENOENT; mlog_errno(status); goto leave; } ocfs2_handle_add_inode(handle, orphan_dir_inode); status = ocfs2_meta_lock(orphan_dir_inode, handle, &orphan_dir_bh, 1); if (status < 0) { mlog_errno(status); goto leave; } status = ocfs2_prepare_dir_for_insert(osb, orphan_dir_inode, orphan_dir_bh, name, OCFS2_ORPHAN_NAMELEN, de_bh); if (status < 0) { mlog_errno(status); goto leave; }leave: if (orphan_dir_inode) iput(orphan_dir_inode); if (orphan_dir_bh) brelse(orphan_dir_bh); mlog_exit(status); return status;}static int ocfs2_orphan_add(struct ocfs2_super *osb, struct ocfs2_journal_handle *handle, struct inode *inode, struct ocfs2_dinode *fe, char *name, struct buffer_head *de_bh){ struct inode *orphan_dir_inode = NULL; struct buffer_head *orphan_dir_bh = NULL; int status = 0; struct ocfs2_dinode *orphan_fe; mlog_entry("(inode->i_ino = %lu)\n", inode->i_ino); orphan_dir_inode = ocfs2_get_system_file_inode(osb, ORPHAN_DIR_SYSTEM_INODE, osb->slot_num); if (!orphan_dir_inode) { status = -ENOENT; mlog_errno(status); goto leave; } status = ocfs2_read_block(osb, OCFS2_I(orphan_dir_inode)->ip_blkno, &orphan_dir_bh, OCFS2_BH_CACHED, orphan_dir_inode); if (status < 0) { mlog_errno(status); goto leave; } status = ocfs2_journal_access(handle, orphan_dir_inode, orphan_dir_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); goto leave; } /* we're a cluster, and nlink can change on disk from * underneath us... */ orphan_fe = (struct ocfs2_dinode *) orphan_dir_bh->b_data; if (S_ISDIR(inode->i_mode)) le16_add_cpu(&orphan_fe->i_links_count, 1); orphan_dir_inode->i_nlink = le16_to_cpu(orphan_fe->i_links_count); status = ocfs2_journal_dirty(handle, orphan_dir_bh); if (status < 0) { mlog_errno(status); goto leave; } status = __ocfs2_add_entry(handle, orphan_dir_inode, name, OCFS2_ORPHAN_NAMELEN, inode, OCFS2_I(inode)->ip_blkno, orphan_dir_bh, de_bh); if (status < 0) { mlog_errno(status); goto leave; } le32_add_cpu(&fe->i_flags, OCFS2_ORPHANED_FL); /* Record which orphan dir our inode now resides * in. delete_inode will use this to determine which orphan * dir to lock. */ spin_lock(&OCFS2_I(inode)->ip_lock); OCFS2_I(inode)->ip_orphaned_slot = osb->slot_num; spin_unlock(&OCFS2_I(inode)->ip_lock); mlog(0, "Inode %"MLFu64" orphaned in slot %d\n", OCFS2_I(inode)->ip_blkno, osb->slot_num);leave: if (orphan_dir_inode) iput(orphan_dir_inode); if (orphan_dir_bh) brelse(orphan_dir_bh); mlog_exit(status); return status;}/* unlike orphan_add, we expect the orphan dir to already be locked here. */int ocfs2_orphan_del(struct ocfs2_super *osb, struct ocfs2_journal_handle *handle, struct inode *orphan_dir_inode, struct inode *inode, struct buffer_head *orphan_dir_bh){ char name[OCFS2_ORPHAN_NAMELEN + 1]; struct ocfs2_dinode *orphan_fe; int status = 0; struct buffer_head *target_de_bh = NULL; struct ocfs2_dir_entry *target_de = NULL; mlog_entry_void(); status = ocfs2_blkno_stringify(OCFS2_I(inode)->ip_blkno, name); if (status < 0) { mlog_errno(status); goto leave; } mlog(0, "removing '%s' from orphan dir %"MLFu64" (namelen=%d)\n", name, OCFS2_I(orphan_dir_inode)->ip_blkno, OCFS2_ORPHAN_NAMELEN); /* find it's spot in the orphan directory */ target_de_bh = ocfs2_find_entry(name, OCFS2_ORPHAN_NAMELEN, orphan_dir_inode, &target_de); if (!target_de_bh) { status = -ENOENT; mlog_errno(status); goto leave; } /* remove it from the orphan directory */ status = ocfs2_delete_entry(handle, orphan_dir_inode, target_de, target_de_bh); if (status < 0) { mlog_errno(status); goto leave; } status = ocfs2_journal_access(handle,orphan_dir_inode, orphan_dir_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); goto leave; } /* do the i_nlink dance! :) */ orphan_fe = (struct ocfs2_dinode *) orphan_dir_bh->b_data; if (S_ISDIR(inode->i_mode)) le16_add_cpu(&orphan_fe->i_links_count, -1); orphan_dir_inode->i_nlink = le16_to_cpu(orphan_fe->i_links_count); status = ocfs2_journal_dirty(handle, orphan_dir_bh); if (status < 0) { mlog_errno(status); goto leave; }leave: if (target_de_bh) brelse(target_de_bh); mlog_exit(status); return status;}struct inode_operations ocfs2_dir_iops = { .create = ocfs2_create, .lookup = ocfs2_lookup, .link = ocfs2_link, .unlink = ocfs2_unlink, .rmdir = ocfs2_unlink, .symlink = ocfs2_symlink, .mkdir = ocfs2_mkdir, .mknod = ocfs2_mknod, .rename = ocfs2_rename, .setattr = ocfs2_setattr, .getattr = ocfs2_getattr,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -