2 * $Id: asingle.c,v 1.8.6.1.4.3 2006-02-26 22:41:19 didg Exp $
7 #endif /* HAVE_CONFIG_H */
12 #include <sys/param.h>
15 #endif /* HAVE_FCNTL_H */
22 #endif /* HAVE_UNISTD_H */
23 #include <atalk/adouble.h>
24 #include <netatalk/endian.h>
28 /* String used to indicate standard input instead of a disk
29 file. Should be a string not normally used for a file
40 /* This structure holds an entry description, consisting of three
41 four byte entities. The first is the Entry ID, the second is
42 the File Offset and the third is the Length.
46 /* Both input and output routines use this struct and the
47 following globals; therefore this module can only be used
48 for one of the two functions at a time.
50 struct single_file_data {
52 char path[ MAXPATHLEN + 1];
53 struct ad_entry entry[ ADEID_MAX ];
56 extern char *forkname[];
57 u_char header_buf[ AD_HEADER_LEN ];
60 * single_open must be called first. pass it a filename that is supposed
61 * to contain a AppleSingle file. an single struct will be allocated and
62 * somewhat initialized; single_filed is set.
65 int single_open( singlefile, flags, fh, options )
67 int flags, options _U_;
72 if ( flags == O_RDONLY ) {
73 if ( strcmp( singlefile, STDIN ) == 0 ) {
74 single.filed = fileno( stdin );
75 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
79 strncpy( single.path, singlefile, MAXPATHLEN );
81 fprintf( stderr, "opened %s for read\n", single.path );
83 if ((( rc = single_header_test()) > 0 ) &&
84 ( single_header_read( fh, rc ) == 0 )) {
94 * single_close must be called before a second file can be opened using
95 * single_open. Upon successful completion, a value of 0 is returned.
96 * Otherwise, a value of -1 is returned.
99 int single_close( keepflag )
102 if ( keepflag == KEEP ) {
103 return( close( single.filed ));
104 } else if ( keepflag == TRASH ) {
105 if (( strcmp( single.path, STDIN ) != 0 ) &&
106 ( unlink( single.path ) < 0 )) {
107 perror ( single.path );
114 * single_header_read is called by single_open, and before any information
115 * can read from the fh substruct. it must be called before any of the
116 * bytes of the other two forks can be read, as well.
119 int single_header_read( fh, version )
124 * entry_buf is used for reading in entry descriptors, and for reading in
125 * the actual entries of FILEINFO, FINDERINFO, and DATES.
127 u_char entry_buf[ 16 ];
129 u_int32_t time_seconds;
130 u_short mask = 0xfcee;
138 * Go through and initialize the array of entry_info structs. Read in the
139 * number of entries, and then read in the info for each entry and save it
143 for ( n = 0 ; n < ADEID_MAX; n++ ) {
144 single.entry[ n ].ade_off = 0;
145 single.entry[ n ].ade_len = 0;
147 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
148 num_entries = ntohs( num_entries );
150 fprintf( stderr, "The number of entries is %d\n", num_entries );
152 for ( ; num_entries > 0 ; num_entries-- ) {
153 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
155 perror( "Premature end of file :" );
158 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
159 entry_id = ntohl( entry_id );
160 memcpy(&single.entry[ entry_id ].ade_off,
162 sizeof( single.entry[ entry_id ].ade_off ));
163 single.entry[ entry_id ].ade_off =
164 ntohl( single.entry[ entry_id ].ade_off );
165 memcpy(&single.entry[ entry_id ].ade_len,
167 sizeof( single.entry[ entry_id ].ade_len ));
168 single.entry[ entry_id ].ade_len =
169 ntohl( single.entry[ entry_id ].ade_len );
171 fprintf( stderr, "entry_id\t%d\n", entry_id );
172 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
173 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
178 * Now that the entries have been identified, check to make sure
179 * it is a Macintosh file if dealing with version two format file.
182 if ( version == 1 ) {
183 if ( single.entry[ ADEID_FILEI ].ade_len > 0 )
184 date_entry = ADEID_FILEI;
185 } else if ( version == 2 ) {
186 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 )
187 date_entry = ADEID_FILEDATESI;
190 fprintf( stderr, "date_entry = %d\n", date_entry );
194 * Go through and copy all the information you can get from
195 * the informational entries into the fh struct. The ENTRYID_DATA
196 * must be the last one done, because it leaves the file pointer in
197 * the right place for the first read of the data fork.
200 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
201 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
204 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
206 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
207 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
208 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
209 perror( "Premature end of file :" );
213 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
214 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
215 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
218 pos = lseek( single.filed,
219 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
220 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
221 sizeof( entry_buf )) {
222 perror( "Premature end of file :" );
225 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
226 sizeof( fh->finder_info.fdType ));
227 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
228 sizeof( fh->finder_info.fdCreator ));
229 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
230 sizeof( fh->finder_info.fdFlags ));
231 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
232 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
233 sizeof( fh->finder_info.fdLocation ));
234 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
235 sizeof( fh->finder_info.fdFldr ));
236 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
237 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
244 strncpy( type, &fh->finder_info.fdType, 4 );
245 strncpy( creator, &fh->finder_info.fdCreator, 4 );
246 type[4] = creator[4] = '\0';
247 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
251 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
252 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
253 fh->comment[0] = '\0';
255 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
257 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
258 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
259 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
260 perror( "Premature end of file :" );
265 * If date_entry is 7, we have an AppleSingle version one, do the
266 * appropriate stuff. If it is 8, we have an AppleSingle version two,
267 * do the right thing. If date_entry is neither, just use the current date.
268 * Unless I can't get the current date, in which case use time zero.
270 if (( date_entry < 7 ) || ( date_entry > 8 )) {
271 if (( time_seconds = time( NULL )) == (u_int32_t)-1 ) {
272 time_seconds = AD_DATE_START;
274 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
276 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
277 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
278 fh->backup_date = AD_DATE_START;
279 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
280 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
283 } else if ( date_entry == ADEID_FILEI ) {
284 pos = lseek( single.filed,
285 single.entry[ date_entry ].ade_off, SEEK_SET );
286 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
287 sizeof( entry_buf )) {
288 perror( "Premature end of file :" );
291 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
292 sizeof( fh->create_date ));
293 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
294 sizeof( fh->mod_date ));
295 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
296 sizeof(fh->backup_date));
297 } else if ( date_entry == ADEID_FILEDATESI ) {
298 pos = lseek( single.filed,
299 single.entry[ date_entry ].ade_off, SEEK_SET );
300 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
301 sizeof( entry_buf )) {
302 perror( "Premature end of file :" );
305 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
306 sizeof( fh->create_date ));
307 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
308 sizeof( fh->mod_date ));
309 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
310 sizeof(fh->backup_date));
312 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
313 fh->forklen[RESOURCE] = 0;
315 fh->forklen[RESOURCE] =
316 htonl( single.entry[ ADEID_RFORK ].ade_len );
318 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
319 fh->forklen[ DATA ] = 0;
321 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
322 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
329 * single_header_test is called from single_open. It checks certain
330 * values of the file and determines if the file is an AppleSingle version
331 * one file something else, and returns a one, or negative one to indicate
334 * The Magic Number of the file, the first four bytes, must be hex
335 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
336 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
337 * are only interested in files from Macs. Therefore these bytes must
338 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
339 * "Macintosh " (that is seven blanks of padding).
341 #define MACINTOSH "Macintosh "
342 u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
343 0, 0, 0, 0, 0, 0, 0, 0 };
345 int single_header_test(void)
350 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
351 if ( cc < (ssize_t)sizeof( header_buf )) {
352 perror( "Premature end of file :" );
356 memcpy( &templong, header_buf, sizeof( templong ));
357 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
358 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
362 memcpy(&templong, header_buf + 4, sizeof( templong ));
363 templong = ntohl( templong );
364 if ( templong == AD_VERSION1 ) {
366 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
368 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
372 } else if ( templong == AD_VERSION2 ) {
374 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
377 "Warning: %s may be a corrupt AppleSingle file.\n",
382 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
391 * single_read is called until it returns zero for each fork. When
392 * it returns zero for the first fork, it seeks to the proper place
393 * to read in the next, if there is one. single_read must be called
394 * enough times to return zero for each fork and no more.
398 int single_read( fork, buffer, length )
411 entry_id = ADEID_DFORK;
414 entry_id = ADEID_RFORK;
421 if (single.entry[entry_id].ade_len > length) {
422 fprintf(stderr, "single_read: Trying to read past end of fork!, length %d, ade_len == %u\n", length, single.entry[entry_id].ade_len);
423 return single.entry[entry_id].ade_len;
425 if ( single.entry[ entry_id ].ade_len == 0 ) {
426 if ( fork == DATA ) {
427 pos = lseek( single.filed,
428 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
433 if ( single.entry[ entry_id ].ade_len < length ) {
434 readlen = single.entry[ entry_id ].ade_len;
440 while (( readlen > 0 ) && ( cc > 0 )) {
441 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
447 cc = buf_ptr - buffer;
448 single.entry[ entry_id ].ade_len -= cc;