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