16 #include <atalk/adouble.h>
17 #include <netatalk/endian.h>
21 /* String used to indicate standard input instead of a disk
22 file. Should be a string not normally used for a file
33 /* This structure holds an entry description, consisting of three
34 four byte entities. The first is the Entry ID, the second is
35 the File Offset and the third is the Length.
39 /* Both input and output routines use this struct and the
40 following globals; therefore this module can only be used
41 for one of the two functions at a time.
43 struct single_file_data {
45 char path[ MAXPATHLEN + 1];
46 struct ad_entry entry[ ADEID_MAX ];
49 extern char *forkname[];
50 u_char header_buf[ AD_HEADER_LEN ];
53 * single_open must be called first. pass it a filename that is supposed
54 * to contain a AppleSingle file. an single struct will be allocated and
55 * somewhat initialized; single_filed is set.
58 int single_open( singlefile, flags, fh, options )
65 if ( flags == O_RDONLY ) {
66 if ( strcmp( singlefile, STDIN ) == 0 ) {
67 single.filed = fileno( stdin );
68 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
72 strncpy( single.path, singlefile, MAXPATHLEN );
74 fprintf( stderr, "opened %s for read\n", single.path );
76 if ((( rc = single_header_test()) > 0 ) &&
77 ( single_header_read( fh, rc ) == 0 )) {
87 * single_close must be called before a second file can be opened using
88 * single_open. Upon successful completion, a value of 0 is returned.
89 * Otherwise, a value of -1 is returned.
92 int single_close( keepflag )
95 if ( keepflag == KEEP ) {
96 return( close( single.filed ));
97 } else if ( keepflag == TRASH ) {
98 if (( strcmp( single.path, STDIN ) != 0 ) &&
99 ( unlink( single.path ) < 0 )) {
100 perror ( single.path );
107 * single_header_read is called by single_open, and before any information
108 * can read from the fh substruct. it must be called before any of the
109 * bytes of the other two forks can be read, as well.
112 int single_header_read( fh, version )
117 * entry_buf is used for reading in entry descriptors, and for reading in
118 * the actual entries of FILEINFO, FINDERINFO, and DATES.
120 u_char entry_buf[ 16 ];
122 u_int32_t time_seconds;
123 u_short mask = 0xfcee;
131 * Go through and initialize the array of entry_info structs. Read in the
132 * number of entries, and then read in the info for each entry and save it
136 for ( n = 0 ; n < ADEID_MAX; n++ ) {
137 single.entry[ n ].ade_off = 0;
138 single.entry[ n ].ade_len = 0;
140 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
141 num_entries = ntohs( num_entries );
143 fprintf( stderr, "The number of entries is %d\n", num_entries );
145 for ( ; num_entries > 0 ; num_entries-- ) {
146 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
148 perror( "Premature end of file :" );
151 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
152 entry_id = ntohl( entry_id );
153 memcpy(&single.entry[ entry_id ].ade_off,
155 sizeof( single.entry[ entry_id ].ade_off ));
156 single.entry[ entry_id ].ade_off =
157 ntohl( single.entry[ entry_id ].ade_off );
158 memcpy(&single.entry[ entry_id ].ade_len,
160 sizeof( single.entry[ entry_id ].ade_len ));
161 single.entry[ entry_id ].ade_len =
162 ntohl( single.entry[ entry_id ].ade_len );
164 fprintf( stderr, "entry_id\t%d\n", entry_id );
165 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
166 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
171 * Now that the entries have been identified, check to make sure
172 * it is a Macintosh file if dealing with version two format file.
175 if ( version == 1 ) {
176 if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
177 date_entry = ADEID_FILEI;
181 } else if ( version == 2 ) {
182 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
183 date_entry = ADEID_FILEDATESI;
189 fprintf( stderr, "date_entry = %d\n", date_entry );
193 * Go through and copy all the information you can get from
194 * the informational entries into the fh struct. The ENTRYID_DATA
195 * must be the last one done, because it leaves the file pointer in
196 * the right place for the first read of the data fork.
199 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
200 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
203 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
205 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
206 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
207 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
208 perror( "Premature end of file :" );
212 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
213 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
214 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
217 pos = lseek( single.filed,
218 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
219 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
220 sizeof( entry_buf )) {
221 perror( "Premature end of file :" );
224 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
225 sizeof( fh->finder_info.fdType ));
226 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
227 sizeof( fh->finder_info.fdCreator ));
228 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
229 sizeof( fh->finder_info.fdFlags ));
230 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
231 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
232 sizeof( fh->finder_info.fdLocation ));
233 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
234 sizeof( fh->finder_info.fdFldr ));
235 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
236 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
243 strncpy( type, &fh->finder_info.fdType, 4 );
244 strncpy( creator, &fh->finder_info.fdCreator, 4 );
245 type[4] = creator[4] = '\0';
246 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
250 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
251 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
252 fh->comment[0] = '\0';
254 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
256 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
257 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
258 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
259 perror( "Premature end of file :" );
264 * If date_entry is 7, we have an AppleSingle version one, do the
265 * appropriate stuff. If it is 8, we have an AppleSingle version two,
266 * do the right thing. If date_entry is neither, just use the current date.
267 * Unless I can't get the current date, in which case use time zero.
269 if (( date_entry < 7 ) || ( date_entry > 8 )) {
270 if (( time_seconds = time( NULL )) == -1 ) {
271 time_seconds = AD_DATE_START;
273 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
275 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
276 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
277 fh->backup_date = AD_DATE_START;
278 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
279 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
282 } else if ( date_entry == ADEID_FILEI ) {
283 pos = lseek( single.filed,
284 single.entry[ date_entry ].ade_off, SEEK_SET );
285 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
286 sizeof( entry_buf )) {
287 perror( "Premature end of file :" );
290 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
291 sizeof( fh->create_date ));
292 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
293 sizeof( fh->mod_date ));
294 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
295 sizeof(fh->backup_date));
296 } else if ( date_entry == ADEID_FILEDATESI ) {
297 pos = lseek( single.filed,
298 single.entry[ date_entry ].ade_off, SEEK_SET );
299 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
300 sizeof( entry_buf )) {
301 perror( "Premature end of file :" );
304 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
305 sizeof( fh->create_date ));
306 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
307 sizeof( fh->mod_date ));
308 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
309 sizeof(fh->backup_date));
311 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
312 fh->forklen[ ADEID_RFORK ] = 0;
314 fh->forklen[ ADEID_RFORK ] =
315 htonl( single.entry[ ADEID_RFORK ].ade_len );
317 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
318 fh->forklen[ DATA ] = 0;
320 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
321 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
328 * single_header_test is called from single_open. It checks certain
329 * values of the file and determines if the file is an AppleSingle version
330 * one file something else, and returns a one, or negative one to indicate
333 * The Magic Number of the file, the first four bytes, must be hex
334 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
335 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
336 * are only interested in files from Macs. Therefore these bytes must
337 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
338 * "Macintosh " (that is seven blanks of padding).
340 #define MACINTOSH "Macintosh "
341 u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
342 0, 0, 0, 0, 0, 0, 0, 0 };
344 int single_header_test(void)
349 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
350 if ( cc < sizeof( header_buf )) {
351 perror( "Premature end of file :" );
355 memcpy( &templong, header_buf, sizeof( templong ));
356 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
357 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
361 memcpy(&templong, header_buf + 4, sizeof( templong ));
362 templong = ntohl( templong );
363 if ( templong == AD_VERSION1 ) {
365 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
367 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
371 } else if ( templong == AD_VERSION2 ) {
373 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
376 "Warning: %s may be a corrupt AppleSingle file.\n",
381 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
390 * single_read is called until it returns zero for each fork. When
391 * it returns zero for the first fork, it seeks to the proper place
392 * to read in the next, if there is one. single_read must be called
393 * enough times to return zero for each fork and no more.
397 int single_read( fork, buffer, length )
410 entry_id = ADEID_DFORK;
413 entry_id = ADEID_RFORK;
420 if ( single.entry[ entry_id ].ade_len < 0 ) {
421 fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
422 return( single.entry[ entry_id ].ade_len );
424 if ( single.entry[ entry_id ].ade_len == 0 ) {
425 if ( fork == DATA ) {
426 pos = lseek( single.filed,
427 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
432 if ( single.entry[ entry_id ].ade_len < length ) {
433 readlen = single.entry[ entry_id ].ade_len;
439 while (( readlen > 0 ) && ( cc > 0 )) {
440 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
446 cc = buf_ptr - buffer;
447 single.entry[ entry_id ].ade_len -= cc;