1 /* 2 * link.c --- create links in a ext2fs directory 3 * 4 * Copyright (C) 1993, 1994 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Public 8 * License. 9 * %End-Header% 10 */ 11 12 #include <stdio.h> 13 #include <string.h> 14 #if HAVE_UNISTD_H 15 #include <unistd.h> 16 #endif 17 18 #include "ext2_fs.h" 19 #include "ext2fs.h" 20 21 struct link_struct { 22 const char *name; 23 int namelen; 24 ext2_ino_t inode; 25 int flags; 26 int done; 27 struct ext2_super_block *sb; 28 }; 29 30 static int link_proc(struct ext2_dir_entry *dirent, 31 int offset, 32 int blocksize, 33 char *buf, 34 void *priv_data) 35 { 36 struct link_struct *ls = (struct link_struct *) priv_data; 37 struct ext2_dir_entry *next; 38 int rec_len, min_rec_len; 39 int ret = 0; 40 41 rec_len = EXT2_DIR_REC_LEN(ls->namelen); 42 43 /* 44 * See if the following directory entry (if any) is unused; 45 * if so, absorb it into this one. 46 */ 47 next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); 48 if ((offset + dirent->rec_len < blocksize - 8) && 49 (next->inode == 0) && 50 (offset + dirent->rec_len + next->rec_len <= blocksize)) { 51 dirent->rec_len += next->rec_len; 52 ret = DIRENT_CHANGED; 53 } 54 55 /* 56 * If the directory entry is used, see if we can split the 57 * directory entry to make room for the new name. If so, 58 * truncate it and return. 59 */ 60 if (dirent->inode) { 61 min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); 62 if (dirent->rec_len < (min_rec_len + rec_len)) 63 return ret; 64 rec_len = dirent->rec_len - min_rec_len; 65 dirent->rec_len = min_rec_len; 66 next = (struct ext2_dir_entry *) (buf + offset + 67 dirent->rec_len); 68 next->inode = 0; 69 next->name_len = 0; 70 next->rec_len = rec_len; 71 return DIRENT_CHANGED; 72 } 73 74 /* 75 * If we get this far, then the directory entry is not used. 76 * See if we can fit the request entry in. If so, do it. 77 */ 78 if (dirent->rec_len < rec_len) 79 return ret; 80 dirent->inode = ls->inode; 81 dirent->name_len = ls->namelen; 82 strncpy(dirent->name, ls->name, ls->namelen); 83 if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) 84 dirent->name_len |= (ls->flags & 0x7) << 8; 85 86 ls->done++; 87 return DIRENT_ABORT|DIRENT_CHANGED; 88 } 89 90 /* 91 * Note: the low 3 bits of the flags field are used as the directory 92 * entry filetype. 93 */ 94 #ifdef __TURBOC__ 95 #pragma argsused 96 #endif 97 errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, 98 ext2_ino_t ino, int flags) 99 { 100 errcode_t retval; 101 struct link_struct ls; 102 struct ext2_inode inode; 103 104 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 105 106 if (!(fs->flags & EXT2_FLAG_RW)) 107 return EXT2_ET_RO_FILSYS; 108 109 ls.name = name; 110 ls.namelen = name ? strlen(name) : 0; 111 ls.inode = ino; 112 ls.flags = flags; 113 ls.done = 0; 114 ls.sb = fs->super; 115 116 retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, 117 0, link_proc, &ls); 118 if (retval) 119 return retval; 120 121 if (!ls.done) 122 return EXT2_ET_DIR_NO_SPACE; 123 124 if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) 125 return retval; 126 127 if (inode.i_flags & EXT2_INDEX_FL) { 128 inode.i_flags &= ~EXT2_INDEX_FL; 129 if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) 130 return retval; 131 } 132 133 return 0; 134 } 135