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