|
| 1 | +/* |
| 2 | + * FSExchangeObjectsCompat.c |
| 3 | + * based on MoreFilesX |
| 4 | + */ |
| 5 | + |
| 6 | +#include "FSExchangeObjectsCompat.h" |
| 7 | +#include <sys/attr.h> |
| 8 | +#include <sys/stat.h> |
| 9 | +#include <sys/mount.h> |
| 10 | + |
| 11 | +__private_extern__ u_int32_t volumeCapabilities(const char *path) |
| 12 | +{ |
| 13 | + struct attrlist alist; |
| 14 | + bzero(&alist, sizeof(alist)); |
| 15 | + alist.bitmapcount = ATTR_BIT_MAP_COUNT; |
| 16 | + alist.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES; // XXX: VOL_INFO must always be set |
| 17 | + |
| 18 | + struct { |
| 19 | + u_int32_t v_size; |
| 20 | + /* Fixed storage */ |
| 21 | + vol_capabilities_attr_t v_caps; |
| 22 | + } vinfo; |
| 23 | + bzero(&vinfo, sizeof(vinfo)); |
| 24 | + if (0 == getattrlist(path, &alist, &vinfo, sizeof(vinfo), 0) |
| 25 | + && 0 != (alist.volattr & ATTR_VOL_CAPABILITIES)) { |
| 26 | + return (vinfo.v_caps.capabilities[VOL_CAPABILITIES_INTERFACES]); |
| 27 | + } |
| 28 | + |
| 29 | + return (0); |
| 30 | +} |
| 31 | + |
| 32 | +static OSErr GenerateUniqueHFSUniStr(long *startSeed, const FSRef *dir1, const FSRef *dir2, HFSUniStr255 *uniqueName) { |
| 33 | + OSErr result; |
| 34 | + long i; |
| 35 | + FSRefParam pb; |
| 36 | + FSRef newRef; |
| 37 | + unsigned char hexStr[17] = "0123456789ABCDEF"; |
| 38 | + |
| 39 | + /* set up the parameter block */ |
| 40 | + pb.name = uniqueName->unicode; |
| 41 | + pb.nameLength = 8; /* always 8 characters */ |
| 42 | + pb.textEncodingHint = kTextEncodingUnknown; |
| 43 | + pb.newRef = &newRef; |
| 44 | + |
| 45 | + /* loop until we get fnfErr with a filename in both directories */ |
| 46 | + result = noErr; |
| 47 | + while ( fnfErr != result ) |
| 48 | + { |
| 49 | + /* convert startSeed to 8 character Unicode string */ |
| 50 | + uniqueName->length = 8; |
| 51 | + for ( i = 0; i < 8; ++i ) |
| 52 | + { |
| 53 | + uniqueName->unicode[i] = hexStr[((*startSeed >> ((7-i)*4)) & 0xf)]; |
| 54 | + } |
| 55 | + |
| 56 | + /* try in dir1 */ |
| 57 | + pb.ref = dir1; |
| 58 | + result = PBMakeFSRefUnicodeSync(&pb); |
| 59 | + if ( fnfErr == result ) |
| 60 | + { |
| 61 | + /* try in dir2 */ |
| 62 | + pb.ref = dir2; |
| 63 | + result = PBMakeFSRefUnicodeSync(&pb); |
| 64 | + if ( fnfErr != result ) |
| 65 | + { |
| 66 | + /* exit if anything other than noErr or fnfErr */ |
| 67 | + require_noerr(result, Dir2PBMakeFSRefUnicodeSyncFailed); |
| 68 | + } |
| 69 | + } |
| 70 | + else |
| 71 | + { |
| 72 | + /* exit if anything other than noErr or fnfErr */ |
| 73 | + require_noerr(result, Dir1PBMakeFSRefUnicodeSyncFailed); |
| 74 | + } |
| 75 | + |
| 76 | + /* increment seed for next pass through loop, */ |
| 77 | + /* or for next call to GenerateUniqueHFSUniStr */ |
| 78 | + ++(*startSeed); |
| 79 | + } |
| 80 | + |
| 81 | + /* we have a unique file name which doesn't exist in dir1 or dir2 */ |
| 82 | + result = noErr; |
| 83 | + |
| 84 | +Dir2PBMakeFSRefUnicodeSyncFailed: |
| 85 | +Dir1PBMakeFSRefUnicodeSyncFailed: |
| 86 | + |
| 87 | + return ( result ); |
| 88 | +} |
| 89 | + |
| 90 | +OSErr FSExchangeObjectsEmulate(const FSRef *sourceRef, const FSRef *destRef, FSRef *newSourceRef, FSRef *newDestRef) { |
| 91 | + |
| 92 | + enum { |
| 93 | + /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */ |
| 94 | + kGetCatInformationMask = (kFSCatInfoSettableInfo | |
| 95 | + kFSCatInfoVolume | |
| 96 | + kFSCatInfoParentDirID) & |
| 97 | + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod), |
| 98 | + /* set everything possible except for mod dates */ |
| 99 | + kSetCatinformationMask = kFSCatInfoSettableInfo & |
| 100 | + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod) |
| 101 | + }; |
| 102 | + |
| 103 | + OSErr result; |
| 104 | + FSCatalogInfo sourceCatalogInfo; /* source file's catalog information */ |
| 105 | + FSCatalogInfo destCatalogInfo; /* destination file's catalog information */ |
| 106 | + HFSUniStr255 sourceName; /* source file's Unicode name */ |
| 107 | + HFSUniStr255 destName; /* destination file's Unicode name */ |
| 108 | + FSRef sourceCurrentRef; /* FSRef to current location of source file throughout this function */ |
| 109 | + FSRef destCurrentRef; /* FSRef to current location of destination file throughout this function */ |
| 110 | + FSRef sourceParentRef; /* FSRef to parent directory of source file */ |
| 111 | + FSRef destParentRef; /* FSRef to parent directory of destination file */ |
| 112 | + HFSUniStr255 sourceUniqueName; /* unique name given to source file while exchanging it with destination */ |
| 113 | + HFSUniStr255 destUniqueName; /* unique name given to destination file while exchanging it with source */ |
| 114 | + long theSeed; /* the seed for generating unique names */ |
| 115 | + Boolean sameParentDirs; /* true if source and destinatin parent directory is the same */ |
| 116 | + |
| 117 | + /* check parameters */ |
| 118 | + require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr); |
| 119 | + |
| 120 | + /* output refs and current refs = input refs to start with */ |
| 121 | + memcpy(newSourceRef, sourceRef, sizeof(FSRef)); |
| 122 | + memcpy(&sourceCurrentRef, sourceRef, sizeof(FSRef)); |
| 123 | + |
| 124 | + memcpy(newDestRef, destRef, sizeof(FSRef)); |
| 125 | + memcpy(&destCurrentRef, destRef, sizeof(FSRef)); |
| 126 | + |
| 127 | + /* Note: The compatibility case won't work for files with *Btree control blocks. */ |
| 128 | + /* Right now the only *Btree files are created by the system. */ |
| 129 | + |
| 130 | + /* get all catalog information and Unicode names for each file */ |
| 131 | + result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef); |
| 132 | + require_noerr(result, SourceFSGetCatalogInfoFailed); |
| 133 | + |
| 134 | + result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef); |
| 135 | + require_noerr(result, DestFSGetCatalogInfoFailed); |
| 136 | + |
| 137 | + /* make sure source and destination are on same volume */ |
| 138 | + require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr); |
| 139 | + |
| 140 | + /* make sure both files are *really* files */ |
| 141 | + require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) && |
| 142 | + (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr); |
| 143 | + |
| 144 | + /* generate 2 names that are unique in both directories */ |
| 145 | + theSeed = 0x4a696d4c; /* a fine unlikely filename */ |
| 146 | + |
| 147 | + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName); |
| 148 | + require_noerr(result, GenerateUniqueHFSUniStr1Failed); |
| 149 | + |
| 150 | + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName); |
| 151 | + require_noerr(result, GenerateUniqueHFSUniStr2Failed); |
| 152 | + |
| 153 | + /* rename sourceCurrentRef to sourceUniqueName */ |
| 154 | + result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef); |
| 155 | + require_noerr(result, FSRenameUnicode1Failed); |
| 156 | + memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); |
| 157 | + |
| 158 | + /* rename destCurrentRef to destUniqueName */ |
| 159 | + result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef); |
| 160 | + require_noerr(result, FSRenameUnicode2Failed); |
| 161 | + memcpy(&destCurrentRef, newDestRef, sizeof(FSRef)); |
| 162 | + |
| 163 | + /* are the source and destination parent directories the same? */ |
| 164 | + sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID ); |
| 165 | + if ( !sameParentDirs ) |
| 166 | + { |
| 167 | + /* move source file to dest parent directory */ |
| 168 | + result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef); |
| 169 | + require_noerr(result, FSMoveObject1Failed); |
| 170 | + memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); |
| 171 | + |
| 172 | + /* move dest file to source parent directory */ |
| 173 | + result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef); |
| 174 | + require_noerr(result, FSMoveObject2Failed); |
| 175 | + memcpy(&destCurrentRef, newDestRef, sizeof(FSRef)); |
| 176 | + } |
| 177 | + |
| 178 | + /* At this point, the files are in their new locations (if they were moved). */ |
| 179 | + /* The source file is named sourceUniqueName and is in the directory referred to */ |
| 180 | + /* by destParentRef. The destination file is named destUniqueName and is in the */ |
| 181 | + /* directory referred to by sourceParentRef. */ |
| 182 | + |
| 183 | + /* give source file the dest file's catalog information except for mod dates */ |
| 184 | + result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo); |
| 185 | + require_noerr(result, FSSetCatalogInfo1Failed); |
| 186 | + |
| 187 | + /* give dest file the source file's catalog information except for mod dates */ |
| 188 | + result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo); |
| 189 | + require_noerr(result, FSSetCatalogInfo2Failed); |
| 190 | + |
| 191 | + /* rename source file with dest file's name */ |
| 192 | + result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef); |
| 193 | + require_noerr(result, FSRenameUnicode3Failed); |
| 194 | + memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); |
| 195 | + |
| 196 | + /* rename dest file with source file's name */ |
| 197 | + result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef); |
| 198 | + require_noerr(result, FSRenameUnicode4Failed); |
| 199 | + |
| 200 | + /* we're done with no errors, so swap newSourceRef and newDestRef */ |
| 201 | + memcpy(newSourceRef, newDestRef, sizeof(FSRef)); |
| 202 | + memcpy(newDestRef, &sourceCurrentRef, sizeof(FSRef)); |
| 203 | + |
| 204 | + return ( result ); |
| 205 | + |
| 206 | + /**********************/ |
| 207 | + |
| 208 | + /* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */ |
| 209 | + /* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */ |
| 210 | + /* state and location they ended up in so that both files can be found by the calling code. */ |
| 211 | + |
| 212 | +FSRenameUnicode4Failed: |
| 213 | + |
| 214 | + /* attempt to rename source file to sourceUniqueName */ |
| 215 | + if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) ) |
| 216 | + { |
| 217 | + memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); |
| 218 | + } |
| 219 | + |
| 220 | +FSRenameUnicode3Failed: |
| 221 | + |
| 222 | + /* attempt to restore dest file's catalog information */ |
| 223 | + verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo)); |
| 224 | + |
| 225 | +FSSetCatalogInfo2Failed: |
| 226 | + |
| 227 | + /* attempt to restore source file's catalog information */ |
| 228 | + verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo)); |
| 229 | + |
| 230 | +FSSetCatalogInfo1Failed: |
| 231 | + |
| 232 | + if ( !sameParentDirs ) |
| 233 | + { |
| 234 | + /* attempt to move dest file back to dest directory */ |
| 235 | + if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) ) |
| 236 | + { |
| 237 | + memcpy(&destCurrentRef, newDestRef, sizeof(FSRef)); |
| 238 | + } |
| 239 | + } |
| 240 | + |
| 241 | +FSMoveObject2Failed: |
| 242 | + |
| 243 | + if ( !sameParentDirs ) |
| 244 | + { |
| 245 | + /* attempt to move source file back to source directory */ |
| 246 | + if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) ) |
| 247 | + { |
| 248 | + memcpy(&sourceCurrentRef, newSourceRef, sizeof(FSRef)); |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | +FSMoveObject1Failed: |
| 253 | + |
| 254 | + /* attempt to rename dest file to original name */ |
| 255 | + verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef)); |
| 256 | + |
| 257 | +FSRenameUnicode2Failed: |
| 258 | + |
| 259 | + /* attempt to rename source file to original name */ |
| 260 | + verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef)); |
| 261 | + |
| 262 | +FSRenameUnicode1Failed: |
| 263 | +GenerateUniqueHFSUniStr2Failed: |
| 264 | +GenerateUniqueHFSUniStr1Failed: |
| 265 | +NotAFile: |
| 266 | +NotSameVolume: |
| 267 | +DestFSGetCatalogInfoFailed: |
| 268 | +SourceFSGetCatalogInfoFailed: |
| 269 | +BadParameter: |
| 270 | + |
| 271 | + return ( result ); |
| 272 | +} |
1 | 273 |
|
2 |
| -2.2.8 / 2017-09-19 |
3 |
| -================== |
4 |
| - |
5 |
| - * fix for high sierra |
6 |
| - * HTTPS updates, remove fieldEditor issues for 10.13 |
7 |
| - * version bump |
8 |
| - * Fix not focusing control field when activating from hotkey or statusitem |
9 |
| - * Merge pull request #414 from troy/2.2.5 |
10 |
| - * Clarify that on-the-wire communication with Simplenote is encrypted (#207) |
11 |
| - * bump version numbers |
12 |
| - * statusitem fix |
|
0 commit comments