1 /* Copyright 1996-1998,2000-2002,2005,2007-2009 Alain Knaff.
2 * This file is part of mtools.
3 *
4 * Mtools is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Mtools is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * mmove.c
18 * Renames/moves an MSDOS file
19 *
20 */
21
22
23 #include "sysincludes.h"
24 #include "msdos.h"
25 #include "mtools.h"
26 #include "vfat.h"
27 #include "mainloop.h"
28 #include "plain_io.h"
29 #include "nameclash.h"
30 #include "file.h"
31 #include "fs.h"
32
33 /*
34 * Preserve the file modification times after the fclose()
35 */
36
37 typedef struct Arg_t {
38 const char *fromname;
39 int verbose;
40 MainParam_t mp;
41
42 direntry_t *entry;
43 ClashHandling_t ch;
44 } Arg_t;
45
46
47 /*
48 * Open the named file for read, create the cluster chain, return the
49 * directory structure or NULL on error.
50 */
renameit(dos_name_t * dosname,char * longname UNUSEDP,void * arg0,direntry_t * targetEntry)51 static int renameit(dos_name_t *dosname,
52 char *longname UNUSEDP,
53 void *arg0,
54 direntry_t *targetEntry)
55 {
56 Arg_t *arg = (Arg_t *) arg0;
57 int fat;
58
59 targetEntry->dir = arg->entry->dir;
60 dosnameToDirentry(dosname, &targetEntry->dir);
61
62 if(IS_DIR(targetEntry)) {
63 direntry_t *movedEntry;
64
65 /* get old direntry. It is important that we do this
66 * on the actual direntry which is stored in the file,
67 * and not on a copy, because we will modify it, and the
68 * modification should be visible at file
69 * de-allocation time */
70 movedEntry = getDirentry(arg->mp.File);
71 if(movedEntry->Dir != targetEntry->Dir) {
72 /* we are indeed moving it to a new directory */
73 direntry_t subEntry;
74 Stream_t *oldDir;
75 /* we have a directory here. Change its parent link */
76
77 initializeDirentry(&subEntry, arg->mp.File);
78
79 switch(vfat_lookup(&subEntry, "..", 2, ACCEPT_DIR,
80 NULL, 0, NULL, 0)) {
81 case -1:
82 fprintf(stderr,
83 " Directory has no parent entry\n");
84 break;
85 case -2:
86 return ERROR_ONE;
87 case 0:
88 GET_DATA(targetEntry->Dir, 0, 0, 0, &fat);
89 if (fat == fat32RootCluster(targetEntry->Dir)) {
90 fat = 0;
91 }
92
93 subEntry.dir.start[1] = (fat >> 8) & 0xff;
94 subEntry.dir.start[0] = fat & 0xff;
95 dir_write(&subEntry);
96 if(arg->verbose){
97 fprintf(stderr,
98 "Easy, isn't it? I wonder why DOS can't do this.\n");
99 }
100 break;
101 }
102
103 wipeEntry(movedEntry);
104
105 /* free the old parent, allocate the new one. */
106 oldDir = movedEntry->Dir;
107 *movedEntry = *targetEntry;
108 COPY(targetEntry->Dir);
109 FREE(&oldDir);
110 return 0;
111 }
112 }
113
114 /* wipe out original entry */
115 wipeEntry(arg->mp.direntry);
116 return 0;
117 }
118
119
120
rename_file(direntry_t * entry,MainParam_t * mp)121 static int rename_file(direntry_t *entry, MainParam_t *mp)
122 /* rename a messy DOS file to another messy DOS file */
123 {
124 int result;
125 Stream_t *targetDir;
126 char *shortname;
127 const char *longname;
128
129 Arg_t * arg = (Arg_t *) (mp->arg);
130
131 arg->entry = entry;
132 targetDir = mp->targetDir;
133
134 if (targetDir == entry->Dir){
135 arg->ch.ignore_entry = -1;
136 arg->ch.source = entry->entry;
137 arg->ch.source_entry = entry->entry;
138 } else {
139 arg->ch.ignore_entry = -1;
140 arg->ch.source = -2;
141 }
142
143 longname = mpPickTargetName(mp);
144 shortname = 0;
145 result = mwrite_one(targetDir, longname, shortname,
146 renameit, (void *)arg, &arg->ch);
147 if(result == 1)
148 return GOT_ONE;
149 else
150 return ERROR_ONE;
151 }
152
153
rename_directory(direntry_t * entry,MainParam_t * mp)154 static int rename_directory(direntry_t *entry, MainParam_t *mp)
155 {
156 int ret;
157
158 /* moves a DOS dir */
159 if(isSubdirOf(mp->targetDir, mp->File)) {
160 fprintf(stderr, "Cannot move directory ");
161 fprintPwd(stderr, entry,0);
162 fprintf(stderr, " into one of its own subdirectories (");
163 fprintPwd(stderr, getDirentry(mp->targetDir),0);
164 fprintf(stderr, ")\n");
165 return ERROR_ONE;
166 }
167
168 if(entry->entry == -3) {
169 fprintf(stderr, "Cannot move a root directory: ");
170 fprintPwd(stderr, entry,0);
171 return ERROR_ONE;
172 }
173
174 ret = rename_file(entry, mp);
175 if(ret & ERROR_ONE)
176 return ret;
177
178 return ret;
179 }
180
rename_oldsyntax(direntry_t * entry,MainParam_t * mp)181 static int rename_oldsyntax(direntry_t *entry, MainParam_t *mp)
182 {
183 int result;
184 Stream_t *targetDir;
185 const char *shortname, *longname;
186
187 Arg_t * arg = (Arg_t *) (mp->arg);
188 arg->entry = entry;
189 targetDir = entry->Dir;
190
191 arg->ch.ignore_entry = -1;
192 arg->ch.source = entry->entry;
193 arg->ch.source_entry = entry->entry;
194
195 #if 0
196 if(!strcasecmp(mp->shortname, arg->fromname)){
197 longname = mp->longname;
198 shortname = mp->targetName;
199 } else {
200 #endif
201 longname = mp->targetName;
202 shortname = 0;
203 #if 0
204 }
205 #endif
206 result = mwrite_one(targetDir, longname, shortname,
207 renameit, (void *)arg, &arg->ch);
208 if(result == 1)
209 return GOT_ONE;
210 else
211 return ERROR_ONE;
212 }
213
214
215 static void usage(int ret) NORETURN;
usage(int ret)216 static void usage(int ret)
217 {
218 fprintf(stderr,
219 "Mtools version %s, dated %s\n", mversion, mdate);
220 fprintf(stderr,
221 "Usage: %s [-vV] [-D clash_option] file targetfile\n", progname);
222 fprintf(stderr,
223 " %s [-vV] [-D clash_option] file [files...] target_directory\n",
224 progname);
225 exit(ret);
226 }
227
228 void mmove(int argc, char **argv, int oldsyntax) NORETURN;
mmove(int argc,char ** argv,int oldsyntax)229 void mmove(int argc, char **argv, int oldsyntax)
230 {
231 Arg_t arg;
232 int c;
233 char shortname[12*4+1];
234 char longname[4*MAX_VNAMELEN+1];
235 char def_drive;
236 int i;
237
238 /* get command line options */
239
240 init_clash_handling(& arg.ch);
241
242 /* get command line options */
243 arg.verbose = 0;
244 if(helpFlag(argc, argv))
245 usage(0);
246 while ((c = getopt(argc, argv, "i:vD:oh")) != EOF) {
247 switch (c) {
248 case 'i':
249 set_cmd_line_image(optarg);
250 break;
251 case 'v': /* dummy option for mcopy */
252 arg.verbose = 1;
253 break;
254 case 'o':
255 handle_clash_options(&arg.ch, c);
256 break;
257 case 'D':
258 if(handle_clash_options(&arg.ch, *optarg))
259 usage(1);
260 break;
261 case 'h':
262 usage(0);
263 case '?':
264 usage(1);
265 default:
266 break;
267 }
268 }
269
270 if (argc - optind < 2)
271 usage(1);
272
273 init_mp(&arg.mp);
274 arg.mp.arg = (void *) &arg;
275 arg.mp.openflags = O_RDWR;
276
277 /* look for a default drive */
278 def_drive = '\0';
279 for(i=optind; i<argc; i++)
280 if(argv[i][0] && argv[i][1] == ':' ){
281 if(!def_drive)
282 def_drive = ch_toupper(argv[i][0]);
283 else if(def_drive != ch_toupper(argv[i][0])){
284 fprintf(stderr,
285 "Cannot move files across different drives\n");
286 exit(1);
287 }
288 }
289
290 if(def_drive)
291 *(arg.mp.mcwd) = def_drive;
292
293 if (oldsyntax && (argc - optind != 2 || strpbrk(":/", argv[argc-1])))
294 oldsyntax = 0;
295
296 arg.mp.lookupflags =
297 ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS | NO_UNIX;
298
299 if (!oldsyntax){
300 target_lookup(&arg.mp, argv[argc-1]);
301 arg.mp.callback = rename_file;
302 arg.mp.dirCallback = rename_directory;
303 } else {
304 /* do not look up the target; it will be the same dir as the
305 * source */
306 arg.fromname = argv[optind];
307 if(arg.fromname[0] && arg.fromname[1] == ':')
308 arg.fromname += 2;
309 arg.fromname = _basename(arg.fromname);
310 arg.mp.targetName = strdup(argv[argc-1]);
311 arg.mp.callback = rename_oldsyntax;
312 }
313
314
315 arg.mp.longname.data = longname;
316 arg.mp.longname.len = sizeof(longname);
317 longname[0]='\0';
318
319 arg.mp.shortname.data = shortname;
320 arg.mp.shortname.len = sizeof(shortname);
321 shortname[0]='\0';
322
323 exit(main_loop(&arg.mp, argv + optind, argc - optind - 1));
324 }
325