Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 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 | */ |
| 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; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 57 | uint32_t fat; |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 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 |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 68 | * modification should be visible at file |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 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 */ |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 76 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 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); |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 104 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 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 | |
| 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 | |
| 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; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 177 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 178 | return ret; |
| 179 | } |
| 180 | |
| 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; |
| 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, |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 223 | " %s [-vV] [-D clash_option] file [files...] target_directory\n", |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 224 | progname); |
| 225 | exit(ret); |
| 226 | } |
| 227 | |
| 228 | void mmove(int argc, char **argv, int oldsyntax) NORETURN; |
| 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': |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 255 | handle_clash_options(&arg.ch, (char)c); |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 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 | |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 273 | init_mp(&arg.mp); |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 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 | } |