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