2 * $Id: asingle.c,v 1.14 2010-01-27 21:27:53 didg Exp $
7 #endif /* HAVE_CONFIG_H */
12 #include <sys/param.h>
20 #include <atalk/adouble.h>
21 #include <netatalk/endian.h>
25 /* String used to indicate standard input instead of a disk
26 file. Should be a string not normally used for a file
37 /* This structure holds an entry description, consisting of three
38 four byte entities. The first is the Entry ID, the second is
39 the File Offset and the third is the Length.
43 /* Both input and output routines use this struct and the
44 following globals; therefore this module can only be used
45 for one of the two functions at a time.
47 static struct single_file_data {
49 char path[ MAXPATHLEN + 1];
50 struct ad_entry entry[ ADEID_MAX ];
53 extern char *forkname[];
54 static u_char header_buf[ AD_HEADER_LEN ];
57 * single_open must be called first. pass it a filename that is supposed
58 * to contain a AppleSingle file. an single struct will be allocated and
59 * somewhat initialized; single_filed is set.
62 int single_open(char *singlefile, int flags, struct FHeader *fh, int options _U_)
66 if ( flags == O_RDONLY ) {
67 if ( strcmp( singlefile, STDIN ) == 0 ) {
68 single.filed = fileno( stdin );
69 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
73 strncpy( single.path, singlefile, MAXPATHLEN );
75 fprintf( stderr, "opened %s for read\n", single.path );
77 if ((( rc = single_header_test()) > 0 ) &&
78 ( single_header_read( fh, rc ) == 0 )) {
88 * single_close must be called before a second file can be opened using
89 * single_open. Upon successful completion, a value of 0 is returned.
90 * Otherwise, a value of -1 is returned.
93 int single_close( int 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( struct FHeader *fh, int version)
115 * entry_buf is used for reading in entry descriptors, and for reading in
116 * the actual entries of FILEINFO, FINDERINFO, and DATES.
118 u_char entry_buf[ADEDLEN_FINDERI];
120 u_int32_t time_seconds;
121 u_short mask = 0xfcee;
129 * Go through and initialize the array of entry_info structs. Read in the
130 * number of entries, and then read in the info for each entry and save it
134 for ( n = 0 ; n < ADEID_MAX; n++ ) {
135 single.entry[ n ].ade_off = 0;
136 single.entry[ n ].ade_len = 0;
138 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
139 num_entries = ntohs( num_entries );
141 fprintf( stderr, "The number of entries is %d\n", num_entries );
143 for ( ; num_entries > 0 ; num_entries-- ) {
144 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
146 perror( "Premature end of file :" );
149 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
150 entry_id = ntohl( entry_id );
151 memcpy(&single.entry[ entry_id ].ade_off,
153 sizeof( single.entry[ entry_id ].ade_off ));
154 single.entry[ entry_id ].ade_off =
155 ntohl( single.entry[ entry_id ].ade_off );
156 memcpy(&single.entry[ entry_id ].ade_len,
158 sizeof( single.entry[ entry_id ].ade_len ));
159 single.entry[ entry_id ].ade_len =
160 ntohl( single.entry[ entry_id ].ade_len );
162 fprintf( stderr, "entry_id\t%d\n", entry_id );
163 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
164 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
169 * Now that the entries have been identified, check to make sure
170 * it is a Macintosh file if dealing with version two format file.
173 if ( version == 1 ) {
174 if ( single.entry[ ADEID_FILEI ].ade_len > 0 )
175 date_entry = ADEID_FILEI;
176 } else if ( version == 2 ) {
177 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 )
178 date_entry = ADEID_FILEDATESI;
181 fprintf( stderr, "date_entry = %d\n", date_entry );
185 * Go through and copy all the information you can get from
186 * the informational entries into the fh struct. The ENTRYID_DATA
187 * must be the last one done, because it leaves the file pointer in
188 * the right place for the first read of the data fork.
191 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
192 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
195 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
197 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
198 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
199 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
200 perror( "Premature end of file :" );
204 if (( single.entry[ ADEID_FINDERI ].ade_len < ADEDLEN_FINDERI ) ||
205 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
206 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
209 pos = lseek( single.filed,
210 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
211 if ( read( single.filed, (char *)entry_buf, ADEDLEN_FINDERI) != ADEDLEN_FINDERI) {
212 perror( "Premature end of file :" );
215 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
216 sizeof( fh->finder_info.fdType ));
217 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
218 sizeof( fh->finder_info.fdCreator ));
219 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
220 sizeof( fh->finder_info.fdFlags ));
221 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
222 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
223 sizeof( fh->finder_info.fdLocation ));
224 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
225 sizeof( fh->finder_info.fdFldr ));
226 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
227 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
234 strncpy( type, &fh->finder_info.fdType, 4 );
235 strncpy( creator, &fh->finder_info.fdCreator, 4 );
236 type[4] = creator[4] = '\0';
237 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
241 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
242 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
243 fh->comment[0] = '\0';
245 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
247 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
248 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
249 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
250 perror( "Premature end of file :" );
255 * If date_entry is 7, we have an AppleSingle version one, do the
256 * appropriate stuff. If it is 8, we have an AppleSingle version two,
257 * do the right thing. If date_entry is neither, just use the current date.
258 * Unless I can't get the current date, in which case use time zero.
260 if (( date_entry < 7 ) || ( date_entry > 8 )) {
261 if (( time_seconds = time( NULL )) == (u_int32_t)-1 ) {
262 time_seconds = AD_DATE_START;
264 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
266 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
267 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
268 fh->backup_date = AD_DATE_START;
269 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
270 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
273 } else if ( date_entry == ADEID_FILEI ) {
274 pos = lseek( single.filed,
275 single.entry[ date_entry ].ade_off, SEEK_SET );
276 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
277 sizeof( entry_buf )) {
278 perror( "Premature end of file :" );
281 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
282 sizeof( fh->create_date ));
283 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
284 sizeof( fh->mod_date ));
285 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
286 sizeof(fh->backup_date));
287 } else if ( date_entry == ADEID_FILEDATESI ) {
288 pos = lseek( single.filed,
289 single.entry[ date_entry ].ade_off, SEEK_SET );
290 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
291 sizeof( entry_buf )) {
292 perror( "Premature end of file :" );
295 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
296 sizeof( fh->create_date ));
297 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
298 sizeof( fh->mod_date ));
299 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
300 sizeof(fh->backup_date));
302 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
303 fh->forklen[RESOURCE] = 0;
305 fh->forklen[RESOURCE] =
306 htonl( single.entry[ ADEID_RFORK ].ade_len );
308 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
309 fh->forklen[ DATA ] = 0;
311 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
312 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
319 * single_header_test is called from single_open. It checks certain
320 * values of the file and determines if the file is an AppleSingle version
321 * one file something else, and returns a one, or negative one to indicate
324 * The Magic Number of the file, the first four bytes, must be hex
325 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
326 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
327 * are only interested in files from Macs. Therefore these bytes must
328 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
329 * "Macintosh " (that is seven blanks of padding).
331 #define MACINTOSH "Macintosh "
332 static u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
333 0, 0, 0, 0, 0, 0, 0, 0 };
335 int single_header_test(void)
340 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
341 if ( cc < (ssize_t)sizeof( header_buf )) {
342 perror( "Premature end of file :" );
346 memcpy( &templong, header_buf, sizeof( templong ));
347 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
348 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
352 memcpy(&templong, header_buf + 4, sizeof( templong ));
353 templong = ntohl( templong );
354 if ( templong == AD_VERSION1 ) {
356 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
358 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
362 } else if ( templong == AD_VERSION2 ) {
364 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
367 "Warning: %s may be a corrupt AppleSingle file.\n",
372 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
381 * single_read is called until it returns zero for each fork. When
382 * it returns zero for the first fork, it seeks to the proper place
383 * to read in the next, if there is one. single_read must be called
384 * enough times to return zero for each fork and no more.
388 ssize_t single_read( int fork, char *buffer, size_t length)
398 entry_id = ADEID_DFORK;
401 entry_id = ADEID_RFORK;
408 if (single.entry[entry_id].ade_len > 0x7FFFFFFF) {
409 fprintf(stderr, "single_read: Trying to read past end of fork!, ade_len == %u\n", single.entry[entry_id].ade_len);
412 if ( single.entry[ entry_id ].ade_len == 0 ) {
413 if ( fork == DATA ) {
414 pos = lseek( single.filed,
415 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
420 if ( single.entry[ entry_id ].ade_len < length ) {
421 readlen = single.entry[ entry_id ].ade_len;
427 while (( readlen > 0 ) && ( cc > 0 )) {
428 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
434 cc = buf_ptr - buffer;
435 single.entry[ entry_id ].ade_len -= cc;