Whamcloud - gitweb
libext2fs: allocate clusters to files in expand_dir.c and mkjournal.c
[tools/e2fsprogs.git] / lib / ext2fs / link.c
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 Library
8  * General Public License, version 2.
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         ext2_filsys     fs;
23         const char      *name;
24         int             namelen;
25         ext2_ino_t      inode;
26         int             flags;
27         int             done;
28         unsigned int    blocksize;
29         errcode_t       err;
30         struct ext2_super_block *sb;
31 };
32
33 static int link_proc(struct ext2_dir_entry *dirent,
34                      int        offset,
35                      int        blocksize,
36                      char       *buf,
37                      void       *priv_data)
38 {
39         struct link_struct *ls = (struct link_struct *) priv_data;
40         struct ext2_dir_entry *next;
41         unsigned int rec_len, min_rec_len, curr_rec_len;
42         int ret = 0;
43
44         rec_len = EXT2_DIR_REC_LEN(ls->namelen);
45
46         ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
47         if (ls->err)
48                 return DIRENT_ABORT;
49
50         /*
51          * See if the following directory entry (if any) is unused;
52          * if so, absorb it into this one.
53          */
54         next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
55         if ((offset + curr_rec_len < blocksize - 8) &&
56             (next->inode == 0) &&
57             (offset + curr_rec_len + next->rec_len <= blocksize)) {
58                 curr_rec_len += next->rec_len;
59                 ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
60                 if (ls->err)
61                         return DIRENT_ABORT;
62                 ret = DIRENT_CHANGED;
63         }
64
65         /*
66          * If the directory entry is used, see if we can split the
67          * directory entry to make room for the new name.  If so,
68          * truncate it and return.
69          */
70         if (dirent->inode) {
71                 min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
72                 if (curr_rec_len < (min_rec_len + rec_len))
73                         return ret;
74                 rec_len = curr_rec_len - min_rec_len;
75                 ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
76                 if (ls->err)
77                         return DIRENT_ABORT;
78                 next = (struct ext2_dir_entry *) (buf + offset +
79                                                   dirent->rec_len);
80                 next->inode = 0;
81                 next->name_len = 0;
82                 ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
83                 if (ls->err)
84                         return DIRENT_ABORT;
85                 return DIRENT_CHANGED;
86         }
87
88         /*
89          * If we get this far, then the directory entry is not used.
90          * See if we can fit the request entry in.  If so, do it.
91          */
92         if (curr_rec_len < rec_len)
93                 return ret;
94         dirent->inode = ls->inode;
95         dirent->name_len = ls->namelen;
96         strncpy(dirent->name, ls->name, ls->namelen);
97         if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
98                 dirent->name_len |= (ls->flags & 0x7) << 8;
99
100         ls->done++;
101         return DIRENT_ABORT|DIRENT_CHANGED;
102 }
103
104 /*
105  * Note: the low 3 bits of the flags field are used as the directory
106  * entry filetype.
107  */
108 #ifdef __TURBOC__
109  #pragma argsused
110 #endif
111 errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
112                       ext2_ino_t ino, int flags)
113 {
114         errcode_t               retval;
115         struct link_struct      ls;
116         struct ext2_inode       inode;
117
118         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
119
120         if (!(fs->flags & EXT2_FLAG_RW))
121                 return EXT2_ET_RO_FILSYS;
122
123         ls.fs = fs;
124         ls.name = name;
125         ls.namelen = name ? strlen(name) : 0;
126         ls.inode = ino;
127         ls.flags = flags;
128         ls.done = 0;
129         ls.sb = fs->super;
130         ls.blocksize = fs->blocksize;
131         ls.err = 0;
132
133         retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
134                                     0, link_proc, &ls);
135         if (retval)
136                 return retval;
137         if (ls.err)
138                 return ls.err;
139
140         if (!ls.done)
141                 return EXT2_ET_DIR_NO_SPACE;
142
143         if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
144                 return retval;
145
146         if (inode.i_flags & EXT2_INDEX_FL) {
147                 inode.i_flags &= ~EXT2_INDEX_FL;
148                 if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
149                         return retval;
150         }
151
152         return 0;
153 }