2 * $Id: asingle.c,v 1.7 2002-01-04 04:45:47 sibaz Exp $
7 #endif /* HAVE_CONFIG_H */
12 #include <sys/param.h>
15 #endif /* HAVE_FCNTL_H */
18 #include <atalk/logger.h>
23 #endif /* HAVE_UNISTD_H */
24 #include <atalk/adouble.h>
25 #include <netatalk/endian.h>
29 /* String used to indicate standard input instead of a disk
30 file. Should be a string not normally used for a file
41 /* This structure holds an entry description, consisting of three
42 four byte entities. The first is the Entry ID, the second is
43 the File Offset and the third is the Length.
47 /* Both input and output routines use this struct and the
48 following globals; therefore this module can only be used
49 for one of the two functions at a time.
51 struct single_file_data {
53 char path[ MAXPATHLEN + 1];
54 struct ad_entry entry[ ADEID_MAX ];
57 extern char *forkname[];
58 u_char header_buf[ AD_HEADER_LEN ];
61 * single_open must be called first. pass it a filename that is supposed
62 * to contain a AppleSingle file. an single struct will be allocated and
63 * somewhat initialized; single_filed is set.
66 int single_open( singlefile, flags, fh, options )
73 if ( flags == O_RDONLY ) {
74 if ( strcmp( singlefile, STDIN ) == 0 ) {
75 single.filed = fileno( stdin );
76 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
80 strncpy( single.path, singlefile, MAXPATHLEN );
82 fprintf( stderr, "opened %s for read\n", single.path );
84 if ((( rc = single_header_test()) > 0 ) &&
85 ( single_header_read( fh, rc ) == 0 )) {
95 * single_close must be called before a second file can be opened using
96 * single_open. Upon successful completion, a value of 0 is returned.
97 * Otherwise, a value of -1 is returned.
100 int single_close( keepflag )
103 if ( keepflag == KEEP ) {
104 return( close( single.filed ));
105 } else if ( keepflag == TRASH ) {
106 if (( strcmp( single.path, STDIN ) != 0 ) &&
107 ( unlink( single.path ) < 0 )) {
108 perror ( single.path );
115 * single_header_read is called by single_open, and before any information
116 * can read from the fh substruct. it must be called before any of the
117 * bytes of the other two forks can be read, as well.
120 int single_header_read( fh, version )
125 * entry_buf is used for reading in entry descriptors, and for reading in
126 * the actual entries of FILEINFO, FINDERINFO, and DATES.
128 u_char entry_buf[ 16 ];
130 u_int32_t time_seconds;
131 u_short mask = 0xfcee;
139 * Go through and initialize the array of entry_info structs. Read in the
140 * number of entries, and then read in the info for each entry and save it
144 for ( n = 0 ; n < ADEID_MAX; n++ ) {
145 single.entry[ n ].ade_off = 0;
146 single.entry[ n ].ade_len = 0;
148 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
149 num_entries = ntohs( num_entries );
151 fprintf( stderr, "The number of entries is %d\n", num_entries );
153 for ( ; num_entries > 0 ; num_entries-- ) {
154 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
156 perror( "Premature end of file :" );
159 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
160 entry_id = ntohl( entry_id );
161 memcpy(&single.entry[ entry_id ].ade_off,
163 sizeof( single.entry[ entry_id ].ade_off ));
164 single.entry[ entry_id ].ade_off =
165 ntohl( single.entry[ entry_id ].ade_off );
166 memcpy(&single.entry[ entry_id ].ade_len,
168 sizeof( single.entry[ entry_id ].ade_len ));
169 single.entry[ entry_id ].ade_len =
170 ntohl( single.entry[ entry_id ].ade_len );
172 fprintf( stderr, "entry_id\t%d\n", entry_id );
173 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
174 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
179 * Now that the entries have been identified, check to make sure
180 * it is a Macintosh file if dealing with version two format file.
183 if ( version == 1 ) {
184 if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
185 date_entry = ADEID_FILEI;
189 } else if ( version == 2 ) {
190 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
191 date_entry = ADEID_FILEDATESI;
197 fprintf( stderr, "date_entry = %d\n", date_entry );
201 * Go through and copy all the information you can get from
202 * the informational entries into the fh struct. The ENTRYID_DATA
203 * must be the last one done, because it leaves the file pointer in
204 * the right place for the first read of the data fork.
207 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
208 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
211 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
213 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
214 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
215 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
216 perror( "Premature end of file :" );
220 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
221 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
222 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
225 pos = lseek( single.filed,
226 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
227 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
228 sizeof( entry_buf )) {
229 perror( "Premature end of file :" );
232 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
233 sizeof( fh->finder_info.fdType ));
234 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
235 sizeof( fh->finder_info.fdCreator ));
236 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
237 sizeof( fh->finder_info.fdFlags ));
238 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
239 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
240 sizeof( fh->finder_info.fdLocation ));
241 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
242 sizeof( fh->finder_info.fdFldr ));
243 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
244 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
251 strncpy( type, &fh->finder_info.fdType, 4 );
252 strncpy( creator, &fh->finder_info.fdCreator, 4 );
253 type[4] = creator[4] = '\0';
254 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
258 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
259 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
260 fh->comment[0] = '\0';
262 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
264 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
265 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
266 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
267 perror( "Premature end of file :" );
272 * If date_entry is 7, we have an AppleSingle version one, do the
273 * appropriate stuff. If it is 8, we have an AppleSingle version two,
274 * do the right thing. If date_entry is neither, just use the current date.
275 * Unless I can't get the current date, in which case use time zero.
277 if (( date_entry < 7 ) || ( date_entry > 8 )) {
278 if (( time_seconds = time( NULL )) == -1 ) {
279 time_seconds = AD_DATE_START;
281 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
283 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
284 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
285 fh->backup_date = AD_DATE_START;
286 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
287 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
290 } else if ( date_entry == ADEID_FILEI ) {
291 pos = lseek( single.filed,
292 single.entry[ date_entry ].ade_off, SEEK_SET );
293 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
294 sizeof( entry_buf )) {
295 perror( "Premature end of file :" );
298 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
299 sizeof( fh->create_date ));
300 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
301 sizeof( fh->mod_date ));
302 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
303 sizeof(fh->backup_date));
304 } else if ( date_entry == ADEID_FILEDATESI ) {
305 pos = lseek( single.filed,
306 single.entry[ date_entry ].ade_off, SEEK_SET );
307 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
308 sizeof( entry_buf )) {
309 perror( "Premature end of file :" );
312 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
313 sizeof( fh->create_date ));
314 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
315 sizeof( fh->mod_date ));
316 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
317 sizeof(fh->backup_date));
319 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
320 fh->forklen[ ADEID_RFORK ] = 0;
322 fh->forklen[ ADEID_RFORK ] =
323 htonl( single.entry[ ADEID_RFORK ].ade_len );
325 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
326 fh->forklen[ DATA ] = 0;
328 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
329 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
336 * single_header_test is called from single_open. It checks certain
337 * values of the file and determines if the file is an AppleSingle version
338 * one file something else, and returns a one, or negative one to indicate
341 * The Magic Number of the file, the first four bytes, must be hex
342 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
343 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
344 * are only interested in files from Macs. Therefore these bytes must
345 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
346 * "Macintosh " (that is seven blanks of padding).
348 #define MACINTOSH "Macintosh "
349 u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
350 0, 0, 0, 0, 0, 0, 0, 0 };
352 int single_header_test(void)
357 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
358 if ( cc < sizeof( header_buf )) {
359 perror( "Premature end of file :" );
363 memcpy( &templong, header_buf, sizeof( templong ));
364 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
365 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
369 memcpy(&templong, header_buf + 4, sizeof( templong ));
370 templong = ntohl( templong );
371 if ( templong == AD_VERSION1 ) {
373 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
375 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
379 } else if ( templong == AD_VERSION2 ) {
381 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
384 "Warning: %s may be a corrupt AppleSingle file.\n",
389 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
398 * single_read is called until it returns zero for each fork. When
399 * it returns zero for the first fork, it seeks to the proper place
400 * to read in the next, if there is one. single_read must be called
401 * enough times to return zero for each fork and no more.
405 int single_read( fork, buffer, length )
418 entry_id = ADEID_DFORK;
421 entry_id = ADEID_RFORK;
428 if ( single.entry[ entry_id ].ade_len < 0 ) {
429 fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
430 return( single.entry[ entry_id ].ade_len );
432 if ( single.entry[ entry_id ].ade_len == 0 ) {
433 if ( fork == DATA ) {
434 pos = lseek( single.filed,
435 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
440 if ( single.entry[ entry_id ].ade_len < length ) {
441 readlen = single.entry[ entry_id ].ade_len;
447 while (( readlen > 0 ) && ( cc > 0 )) {
448 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
454 cc = buf_ptr - buffer;
455 single.entry[ entry_id ].ade_len -= cc;