1e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall/*
2e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall * symlink.c --- make a symlink in the filesystem, based on mkdir.c
3e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall *
4e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall * Copyright (c) 2012, Intel Corporation.
5e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall * All Rights Reserved.
6e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall *
7e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall * %Begin-Header%
8e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall * This file may be redistributed under the terms of the GNU Library
9e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall * General Public License, version 2.
10e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall * %End-Header%
11e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall */
12e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
13e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include <stdio.h>
14e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include <string.h>
15e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#if HAVE_UNISTD_H
16e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include <unistd.h>
17e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#endif
18e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include <fcntl.h>
19e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include <time.h>
20e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#if HAVE_SYS_STAT_H
21e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include <sys/stat.h>
22e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#endif
23e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#if HAVE_SYS_TYPES_H
24e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include <sys/types.h>
25e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#endif
26e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
27e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include "ext2_fs.h"
28e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall#include "ext2fs.h"
29e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
30e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrallerrcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
31e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			 const char *name, char *target)
32e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall{
33e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	ext2_extent_handle_t	handle;
34e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	errcode_t		retval;
35e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	struct ext2_inode	inode;
36e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	ext2_ino_t		scratch_ino;
37e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	blk64_t			blk;
38e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	int			fastlink;
39e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	unsigned int		target_len;
40e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	char			*block_buf = 0;
41e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
42e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
43e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
44e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/* The Linux kernel doesn't allow for links longer than a block */
45e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	target_len = strlen(target);
46e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (target_len > fs->blocksize) {
47e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = EXT2_ET_INVALID_ARGUMENT;
48e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		goto cleanup;
49e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	}
50e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
51e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/*
52e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * Allocate a data block for slow links
53e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 */
54e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	fastlink = (target_len < sizeof(inode.i_block));
55e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (!fastlink) {
56e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = ext2fs_new_block2(fs, 0, 0, &blk);
57e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (retval)
58e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
59e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = ext2fs_get_mem(fs->blocksize, &block_buf);
60e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (retval)
61e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
62e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	}
63e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
64e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/*
65e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * Allocate an inode, if necessary
66e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 */
67e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (!ino) {
68e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = ext2fs_new_inode(fs, parent, LINUX_S_IFLNK | 0755,
69e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall					  0, &ino);
70e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (retval)
71e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
72e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	}
73e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
74e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/*
75e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * Create the inode structure....
76e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 */
77e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	memset(&inode, 0, sizeof(struct ext2_inode));
78e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	inode.i_mode = LINUX_S_IFLNK | 0777;
79e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	inode.i_uid = inode.i_gid = 0;
80e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	ext2fs_iblk_set(fs, &inode, fastlink ? 0 : 1);
81e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	inode.i_links_count = 1;
82e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	inode.i_size = target_len;
83e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/* The time fields are set by ext2fs_write_new_inode() */
84e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
85e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (fastlink) {
86e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		/* Fast symlinks, target stored in inode */
87e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		strcpy((char *)&inode.i_block, target);
88e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	} else {
89e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		/* Slow symlinks, target stored in the first block */
90e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		memset(block_buf, 0, fs->blocksize);
91e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		strcpy(block_buf, target);
92e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (fs->super->s_feature_incompat &
93e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		    EXT3_FEATURE_INCOMPAT_EXTENTS) {
94e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			/*
95e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			 * The extent bmap is setup after the inode and block
96e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			 * have been written out below.
97e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			 */
98e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			inode.i_flags |= EXT4_EXTENTS_FL;
99e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		}
100e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	}
101e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
102e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/*
103e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * Write out the inode and inode data block.  The inode generation
104e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * number is assigned by write_new_inode, which means that the
105e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * operations using ino must come after it.
106e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 */
107e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	retval = ext2fs_write_new_inode(fs, ino, &inode);
108e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (retval)
109e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		goto cleanup;
110e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
111e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (!fastlink) {
112e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = ext2fs_bmap2(fs, ino, &inode, NULL, BMAP_SET, 0, NULL,
113e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall				      &blk);
114e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (retval)
115e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
116e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
117e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = io_channel_write_blk64(fs->io, blk, 1, block_buf);
118e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (retval)
119e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
120e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	}
121e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
122e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/*
123e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * Link the symlink into the filesystem hierarchy
124e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 */
125e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (name) {
126e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
127e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall				       &scratch_ino);
128e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (!retval) {
129e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			retval = EXT2_ET_FILE_EXISTS;
130e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
131e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		}
132e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (retval != EXT2_ET_FILE_NOT_FOUND)
133e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
134e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_SYMLINK);
135e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		if (retval)
136e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall			goto cleanup;
137e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	}
138e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
139e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	/*
140e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 * Update accounting....
141e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	 */
142e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (!fastlink)
143e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		ext2fs_block_alloc_stats2(fs, blk, +1);
144e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
145e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall
146e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrallcleanup:
147e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	if (block_buf)
148e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall		ext2fs_free_mem(&block_buf);
149e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall	return retval;
150e0ed7404719a9ddd2ba427a80db5365c8bad18c0JP Abgrall}
151