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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
/* -*- mode: c; c-basic-offset: 8; -*-
* vim: noexpandtab sw=8 ts=8 sts=0:
*
* link.c
*
* Create links in OCFS2 directories. For the OCFS2 userspace library.
*
* Copyright (C) 2004 Oracle. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License, version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*
* This code is a port of e2fsprogs/lib/ext2fs/link.c
* Copyright (C) 1993, 1994 Theodore Ts'o.
*/
#define _XOPEN_SOURCE 600 /* Triggers magic in features.h */
#define _LARGEFILE64_SOURCE
#define _DEFAULT_SOURCE
#include <string.h>
#include "ocfs2/ocfs2.h"
struct link_struct {
const char *name;
int namelen;
uint64_t inode;
int flags;
int done;
int blockend; /* What to consider the end
of the block. This handles
the directory trailer if it
exists */
int blkno;
struct ocfs2_dinode *sb;
};
static int link_proc(struct ocfs2_dir_entry *dirent,
uint64_t blocknr,
int offset,
int blocksize,
char *buf,
void *priv_data)
{
struct link_struct *ls = (struct link_struct *) priv_data;
struct ocfs2_dir_entry *next;
int rec_len, min_rec_len;
int ret = 0;
rec_len = OCFS2_DIR_REC_LEN(ls->namelen);
/*
* See if the following directory entry (if any) is unused;
* if so, absorb it into this one.
*/
next = (struct ocfs2_dir_entry *) (buf + offset + dirent->rec_len);
if ((offset + dirent->rec_len < ls->blockend - 8) &&
(next->inode == 0) &&
(offset + dirent->rec_len + next->rec_len <= ls->blockend)) {
dirent->rec_len += next->rec_len;
ret = OCFS2_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 = OCFS2_DIR_REC_LEN(dirent->name_len & 0xFF);
if (dirent->rec_len < (min_rec_len + rec_len))
return ret;
rec_len = dirent->rec_len - min_rec_len;
dirent->rec_len = min_rec_len;
next = (struct ocfs2_dir_entry *) (buf + offset +
dirent->rec_len);
next->inode = 0;
next->name_len = 0;
next->rec_len = rec_len;
return OCFS2_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 (dirent->rec_len < rec_len)
return ret;
dirent->inode = ls->inode;
dirent->name_len = ls->namelen;
strncpy(dirent->name, ls->name, ls->namelen);
dirent->file_type = ls->flags;
ls->blkno = blocknr;
ls->done++;
return OCFS2_DIRENT_ABORT|OCFS2_DIRENT_CHANGED;
}
/*
* Note: the low 3 bits of the flags field are used as the directory
* entry filetype.
*/
errcode_t ocfs2_link(ocfs2_filesys *fs, uint64_t dir, const char *name,
uint64_t ino, int flags)
{
errcode_t retval;
struct link_struct ls;
char *buf;
struct ocfs2_dinode *di;
if (!(fs->fs_flags & OCFS2_FLAG_RW))
return OCFS2_ET_RO_FILESYS;
if ((ino < OCFS2_SUPER_BLOCK_BLKNO) ||
(ino > fs->fs_blocks))
return OCFS2_ET_INVALID_ARGUMENT;
retval = ocfs2_malloc_block(fs->fs_io, &buf);
if (retval)
return retval;
retval = ocfs2_read_inode(fs, dir, buf);
if (retval)
goto out_free;
di = (struct ocfs2_dinode *)buf;
ls.name = name;
ls.namelen = name ? strlen(name) : 0;
ls.inode = ino;
ls.flags = flags;
ls.done = 0;
ls.sb = fs->fs_super;
if (ocfs2_dir_has_trailer(fs, di))
ls.blockend = ocfs2_dir_trailer_blk_off(fs);
else
ls.blockend = fs->fs_blocksize;
retval = ocfs2_dir_iterate(fs, dir,
OCFS2_DIRENT_FLAG_INCLUDE_EMPTY,
NULL, link_proc, &ls);
if (retval)
goto out_free;
if (!ls.done) {
retval = ocfs2_expand_dir(fs, dir);
if (retval)
goto out_free;
/* Gotta refresh */
retval = ocfs2_read_inode(fs, dir, buf);
if (retval)
goto out_free;
if (ocfs2_dir_has_trailer(fs, di))
ls.blockend = ocfs2_dir_trailer_blk_off(fs);
else
ls.blockend = fs->fs_blocksize;
retval = ocfs2_dir_iterate(fs, dir,
OCFS2_DIRENT_FLAG_INCLUDE_EMPTY,
NULL, link_proc, &ls);
if (!retval && !ls.done)
retval = OCFS2_ET_INTERNAL_FAILURE;
}
if (ls.done) {
if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)) &&
(di->i_dyn_features & OCFS2_INDEXED_DIR_FL))
retval = ocfs2_dx_dir_insert_entry(fs, dir, ls.name,
ls.inode, ls.blkno);
}
out_free:
ocfs2_free(&buf);
return retval;
}
|