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