1 /**
2  * Copyright (c) 2008-2015 Alper Akcan <alper.akcan@gmail.com>
3  * Copyright (c) 2009 Renzo Davoli <renzo@cs.unibo.it>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program (in the main directory of the fuse-ext2
17  * distribution in the file COPYING); if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "fuse-ext2.h"
22 
do_modetoext2lag(mode_t mode)23 int do_modetoext2lag (mode_t mode)
24 {
25 	if (S_ISREG(mode)) {
26 		return EXT2_FT_REG_FILE;
27 	} else if (S_ISDIR(mode)) {
28 		return EXT2_FT_DIR;
29 	} else if (S_ISCHR(mode)) {
30 		return EXT2_FT_CHRDEV;
31 	} else if (S_ISBLK(mode)) {
32 		return EXT2_FT_BLKDEV;
33 	} else if (S_ISFIFO(mode)) {
34 		return EXT2_FT_FIFO;
35 	} else if (S_ISSOCK(mode)) {
36 		return EXT2_FT_SOCK;
37 	} else if (S_ISLNK(mode)) {
38 		return EXT2_FT_SYMLINK;
39 	}
40 	return EXT2_FT_UNKNOWN;
41 }
42 
old_valid_dev(dev_t dev)43 static inline int old_valid_dev(dev_t dev)
44 {
45 	return major(dev) < 256 && minor(dev) < 256;
46 }
47 
old_encode_dev(dev_t dev)48 static inline __u16 old_encode_dev(dev_t dev)
49 {
50 	return (major(dev) << 8) | minor(dev);
51 }
52 
new_encode_dev(dev_t dev)53 static inline __u32 new_encode_dev(dev_t dev)
54 {
55 	unsigned major_v = major(dev);
56 	unsigned minor_v = minor(dev);
57 	return (minor_v & 0xff) | (major_v << 8) | ((minor_v & ~0xff) << 12);
58 }
59 
do_create(ext2_filsys e2fs,const char * path,mode_t mode,dev_t dev,const char * fastsymlink)60 int do_create (ext2_filsys e2fs, const char *path, mode_t mode, dev_t dev, const char *fastsymlink)
61 {
62 	int rt;
63 	time_t tm;
64 	errcode_t rc;
65 
66 	char *p_path;
67 	char *r_path;
68 
69 	ext2_ino_t ino;
70 	struct ext2_inode inode;
71 	ext2_ino_t n_ino;
72 
73 	struct fuse_context *ctx;
74 
75 	debugf("enter");
76 	debugf("path = %s, mode: 0%o", path, mode);
77 
78 	rt=do_check_split(path, &p_path, &r_path);
79 
80 	debugf("parent: %s, child: %s", p_path, r_path);
81 
82 	rt = do_readinode(e2fs, p_path, &ino, &inode);
83 	if (rt) {
84 		debugf("do_readinode(%s, &ino, &inode); failed", p_path);
85 		free_split(p_path, r_path);
86 		return rt;
87 	}
88 
89 	rc = ext2fs_new_inode(e2fs, ino, mode, 0, &n_ino);
90 	if (rc) {
91 		debugf("ext2fs_new_inode(ep.fs, ino, mode, 0, &n_ino); failed");
92 		free_split(p_path, r_path);
93 		return -ENOMEM;
94 	}
95 
96 	do {
97 		debugf("calling ext2fs_link(e2fs, %d, %s, %d, %d);", ino, r_path, n_ino, do_modetoext2lag(mode));
98 		rc = ext2fs_link(e2fs, ino, r_path, n_ino, do_modetoext2lag(mode));
99 		if (rc == EXT2_ET_DIR_NO_SPACE) {
100 			debugf("calling ext2fs_expand_dir(e2fs, &d)", ino);
101 			if (ext2fs_expand_dir(e2fs, ino)) {
102 				debugf("error while expanding directory %s (%d)", p_path, ino);
103 				free_split(p_path, r_path);
104 				return -ENOSPC;
105 			}
106 		}
107 	} while (rc == EXT2_ET_DIR_NO_SPACE);
108 	if (rc) {
109 		debugf("ext2fs_link(e2fs, %d, %s, %d, %d); failed", ino, r_path, n_ino, do_modetoext2lag(mode));
110 		free_split(p_path, r_path);
111 		return -EIO;
112 	}
113 
114 	if (ext2fs_test_inode_bitmap(e2fs->inode_map, n_ino)) {
115 		debugf("inode already set");
116 	}
117 
118 	ext2fs_inode_alloc_stats2(e2fs, n_ino, +1, 0);
119 	memset(&inode, 0, sizeof(inode));
120 	tm = e2fs->now ? e2fs->now : time(NULL);
121 	inode.i_mode = mode;
122 	inode.i_atime = inode.i_ctime = inode.i_mtime = tm;
123 	inode.i_links_count = 1;
124 	inode.i_size = 0;
125 	ctx = fuse_get_context();
126 	if (ctx) {
127 		ext2_write_uid(&inode, ctx->uid);
128 		ext2_write_gid(&inode, ctx->gid);
129 	}
130 	if (e2fs->super->s_feature_incompat &
131 	    EXT3_FEATURE_INCOMPAT_EXTENTS) {
132 		int i;
133 		struct ext3_extent_header *eh;
134 
135 		eh = (struct ext3_extent_header *) &inode.i_block[0];
136 		eh->eh_depth = 0;
137 		eh->eh_entries = 0;
138 		eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
139 		i = (sizeof(inode.i_block) - sizeof(*eh)) /
140 			sizeof(struct ext3_extent);
141 		eh->eh_max = ext2fs_cpu_to_le16(i);
142 		inode.i_flags |= EXT4_EXTENTS_FL;
143 	}
144 
145 	if (S_ISCHR(mode) || S_ISBLK(mode)) {
146 		if (old_valid_dev(dev))
147 			inode.i_block[0]= ext2fs_cpu_to_le32(old_encode_dev(dev));
148 		else
149 			inode.i_block[1]= ext2fs_cpu_to_le32(new_encode_dev(dev));
150 	}
151 
152 	if (S_ISLNK(mode) && fastsymlink != NULL) {
153 		inode.i_size = strlen(fastsymlink);
154 		strncpy((char *)&(inode.i_block[0]),fastsymlink,
155 				(EXT2_N_BLOCKS * sizeof(inode.i_block[0])));
156 	}
157 
158 	rc = ext2fs_write_new_inode(e2fs, n_ino, &inode);
159 	if (rc) {
160 		debugf("ext2fs_write_new_inode(e2fs, n_ino, &inode);");
161 		free_split(p_path, r_path);
162 		return -EIO;
163 	}
164 
165 	/* update parent dir */
166 	rt = do_readinode(e2fs, p_path, &ino, &inode);
167 	if (rt) {
168 		debugf("do_readinode(%s, &ino, &inode); dailed", p_path);
169 		free_split(p_path, r_path);
170 		return -EIO;
171 	}
172 	inode.i_ctime = inode.i_mtime = tm;
173 	rc = do_writeinode(e2fs, ino, &inode);
174 	if (rc) {
175 		debugf("do_writeinode(e2fs, ino, &inode); failed");
176 		free_split(p_path, r_path);
177 		return -EIO;
178 	}
179 
180 	free_split(p_path, r_path);
181 
182 	debugf("leave");
183 	return 0;
184 }
185 
op_create(const char * path,mode_t mode,struct fuse_file_info * fi)186 int op_create (const char *path, mode_t mode, struct fuse_file_info *fi)
187 {
188 	int rt;
189 	ext2_filsys e2fs = current_ext2fs();
190 
191 	debugf("enter");
192 	debugf("path = %s, mode: 0%o", path, mode);
193 
194 	if (op_open(path, fi) == 0) {
195 		debugf("leave");
196 		return 0;
197 	}
198 
199 	rt = do_create(e2fs, path, mode, 0, NULL);
200 	if (rt != 0) {
201 		return rt;
202 	}
203 
204 	if (op_open(path, fi)) {
205 		debugf("op_open(path, fi); failed");
206 		return -EIO;
207 	}
208 
209 	debugf("leave");
210 	return 0;
211 }
212