blob: 933dfa83670b9af27e6500fb1542aa2c286aba92 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00002 * link.c --- create links in a ext2fs directory
Theodore Ts'o3839e651997-04-26 13:21:57 +00003 *
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00004 * 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%
Theodore Ts'o3839e651997-04-26 13:21:57 +000010 */
11
12#include <stdio.h>
13#include <string.h>
14#include <unistd.h>
15#include <stdlib.h>
Theodore Ts'of3db3561997-04-26 13:34:30 +000016
Theodore Ts'o3839e651997-04-26 13:21:57 +000017#include <linux/ext2_fs.h>
18
19#include "ext2fs.h"
20
21struct link_struct {
22 const char *name;
23 int namelen;
24 ino_t inode;
25 int flags;
26 int done;
27};
28
29static int link_proc(struct ext2_dir_entry *dirent,
30 int offset,
31 int blocksize,
32 char *buf,
33 void *private)
34{
35 struct link_struct *ls = (struct link_struct *) private;
36 struct ext2_dir_entry *next;
37 int rec_len;
38 int ret = 0;
39
40 rec_len = EXT2_DIR_REC_LEN(ls->namelen);
41
42 /*
43 * See if the following directory entry (if any) is unused;
44 * if so, absorb it into this one.
45 */
46 next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
47 if ((offset + dirent->rec_len < blocksize - 8) &&
48 (next->inode == 0) &&
49 (offset + dirent->rec_len + next->rec_len <= blocksize)) {
50 dirent->rec_len += next->rec_len;
51 ret = DIRENT_CHANGED;
52 }
53
54 /*
55 * If the directory entry is used, see if we can split the
56 * directory entry to make room for the new name. If so,
57 * truncate it and return.
58 */
59 if (dirent->inode) {
60 if (dirent->rec_len < (EXT2_DIR_REC_LEN(dirent->name_len) +
61 rec_len))
62 return ret;
63 rec_len = dirent->rec_len - EXT2_DIR_REC_LEN(dirent->name_len);
64 dirent->rec_len = EXT2_DIR_REC_LEN(dirent->name_len);
65 next = (struct ext2_dir_entry *) (buf + offset +
66 dirent->rec_len);
67 next->inode = 0;
68 next->name_len = 0;
69 next->rec_len = rec_len;
70 return DIRENT_CHANGED;
71 }
72
73 /*
74 * If we get this far, then the directory entry is not used.
75 * See if we can fit the request entry in. If so, do it.
76 */
77 if (dirent->rec_len < rec_len)
78 return ret;
79 dirent->inode = ls->inode;
80 dirent->name_len = ls->namelen;
81 strncpy(dirent->name, ls->name, ls->namelen);
82
83 ls->done++;
84 return DIRENT_ABORT|DIRENT_CHANGED;
85}
86
87errcode_t ext2fs_link(ext2_filsys fs, ino_t dir, const char *name, ino_t ino,
88 int flags)
89{
90 errcode_t retval;
91 struct link_struct ls;
92
Theodore Ts'of3db3561997-04-26 13:34:30 +000093 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
94
Theodore Ts'o3839e651997-04-26 13:21:57 +000095 if (!(fs->flags & EXT2_FLAG_RW))
96 return EXT2_ET_RO_FILSYS;
97
98 ls.name = name;
99 ls.namelen = name ? strlen(name) : 0;
100 ls.inode = ino;
101 ls.flags = 0;
102 ls.done = 0;
103
104 retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
105 0, link_proc, &ls);
106 if (retval)
107 return retval;
108
109 return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
110}