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>
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 static struct single_file_data {
52 char path[ MAXPATHLEN + 1];
53 struct ad_entry entry[ ADEID_MAX ];
56 extern char *forkname[];
57 static 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(char *singlefile, int flags, struct FHeader *fh, int options _U_)
69 if ( flags == O_RDONLY ) {
70 if ( strcmp( singlefile, STDIN ) == 0 ) {
71 single.filed = fileno( stdin );
72 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
76 strncpy( single.path, singlefile, MAXPATHLEN );
78 fprintf( stderr, "opened %s for read\n", single.path );
80 if ((( rc = single_header_test()) > 0 ) &&
81 ( single_header_read( fh, rc ) == 0 )) {
91 * single_close must be called before a second file can be opened using
92 * single_open. Upon successful completion, a value of 0 is returned.
93 * Otherwise, a value of -1 is returned.
96 int single_close( int keepflag)
98 if ( keepflag == KEEP ) {
99 return( close( single.filed ));
100 } else if ( keepflag == TRASH ) {
101 if (( strcmp( single.path, STDIN ) != 0 ) &&
102 ( unlink( single.path ) < 0 )) {
103 perror ( single.path );
110 * single_header_read is called by single_open, and before any information
111 * can read from the fh substruct. it must be called before any of the
112 * bytes of the other two forks can be read, as well.
115 int single_header_read( struct FHeader *fh, int version)
118 * entry_buf is used for reading in entry descriptors, and for reading in
119 * the actual entries of FILEINFO, FINDERINFO, and DATES.
121 u_char entry_buf[ 16 ];
123 u_int32_t time_seconds;
124 u_short mask = 0xfcee;
132 * Go through and initialize the array of entry_info structs. Read in the
133 * number of entries, and then read in the info for each entry and save it
137 for ( n = 0 ; n < ADEID_MAX; n++ ) {
138 single.entry[ n ].ade_off = 0;
139 single.entry[ n ].ade_len = 0;
141 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
142 num_entries = ntohs( num_entries );
144 fprintf( stderr, "The number of entries is %d\n", num_entries );
146 for ( ; num_entries > 0 ; num_entries-- ) {
147 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
149 perror( "Premature end of file :" );
152 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
153 entry_id = ntohl( entry_id );
154 memcpy(&single.entry[ entry_id ].ade_off,
156 sizeof( single.entry[ entry_id ].ade_off ));
157 single.entry[ entry_id ].ade_off =
158 ntohl( single.entry[ entry_id ].ade_off );
159 memcpy(&single.entry[ entry_id ].ade_len,
161 sizeof( single.entry[ entry_id ].ade_len ));
162 single.entry[ entry_id ].ade_len =
163 ntohl( single.entry[ entry_id ].ade_len );
165 fprintf( stderr, "entry_id\t%d\n", entry_id );
166 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
167 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
172 * Now that the entries have been identified, check to make sure
173 * it is a Macintosh file if dealing with version two format file.
176 if ( version == 1 ) {
177 if ( single.entry[ ADEID_FILEI ].ade_len > 0 )
178 date_entry = ADEID_FILEI;
179 } else if ( version == 2 ) {
180 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 )
181 date_entry = ADEID_FILEDATESI;
184 fprintf( stderr, "date_entry = %d\n", date_entry );
188 * Go through and copy all the information you can get from
189 * the informational entries into the fh struct. The ENTRYID_DATA
190 * must be the last one done, because it leaves the file pointer in
191 * the right place for the first read of the data fork.
194 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
195 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
198 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
200 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
201 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
202 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
203 perror( "Premature end of file :" );
207 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
208 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
209 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
212 pos = lseek( single.filed,
213 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
214 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
215 sizeof( entry_buf )) {
216 perror( "Premature end of file :" );
219 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
220 sizeof( fh->finder_info.fdType ));
221 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
222 sizeof( fh->finder_info.fdCreator ));
223 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
224 sizeof( fh->finder_info.fdFlags ));
225 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
226 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
227 sizeof( fh->finder_info.fdLocation ));
228 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
229 sizeof( fh->finder_info.fdFldr ));
230 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
231 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
238 strncpy( type, &fh->finder_info.fdType, 4 );
239 strncpy( creator, &fh->finder_info.fdCreator, 4 );
240 type[4] = creator[4] = '\0';
241 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
245 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
246 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
247 fh->comment[0] = '\0';
249 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
251 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
252 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
253 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
254 perror( "Premature end of file :" );
259 * If date_entry is 7, we have an AppleSingle version one, do the
260 * appropriate stuff. If it is 8, we have an AppleSingle version two,
261 * do the right thing. If date_entry is neither, just use the current date.
262 * Unless I can't get the current date, in which case use time zero.
264 if (( date_entry < 7 ) || ( date_entry > 8 )) {
265 if (( time_seconds = time( NULL )) == (u_int32_t)-1 ) {
266 time_seconds = AD_DATE_START;
268 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
270 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
271 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
272 fh->backup_date = AD_DATE_START;
273 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
274 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
277 } else if ( date_entry == ADEID_FILEI ) {
278 pos = lseek( single.filed,
279 single.entry[ date_entry ].ade_off, SEEK_SET );
280 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
281 sizeof( entry_buf )) {
282 perror( "Premature end of file :" );
285 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
286 sizeof( fh->create_date ));
287 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
288 sizeof( fh->mod_date ));
289 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
290 sizeof(fh->backup_date));
291 } else if ( date_entry == ADEID_FILEDATESI ) {
292 pos = lseek( single.filed,
293 single.entry[ date_entry ].ade_off, SEEK_SET );
294 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
295 sizeof( entry_buf )) {
296 perror( "Premature end of file :" );
299 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
300 sizeof( fh->create_date ));
301 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
302 sizeof( fh->mod_date ));
303 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
304 sizeof(fh->backup_date));
306 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
307 fh->forklen[RESOURCE] = 0;
309 fh->forklen[RESOURCE] =
310 htonl( single.entry[ ADEID_RFORK ].ade_len );
312 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
313 fh->forklen[ DATA ] = 0;
315 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
316 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
323 * single_header_test is called from single_open. It checks certain
324 * values of the file and determines if the file is an AppleSingle version
325 * one file something else, and returns a one, or negative one to indicate
328 * The Magic Number of the file, the first four bytes, must be hex
329 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
330 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
331 * are only interested in files from Macs. Therefore these bytes must
332 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
333 * "Macintosh " (that is seven blanks of padding).
335 #define MACINTOSH "Macintosh "
336 static u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
337 0, 0, 0, 0, 0, 0, 0, 0 };
339 int single_header_test(void)
344 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
345 if ( cc < (ssize_t)sizeof( header_buf )) {
346 perror( "Premature end of file :" );
350 memcpy( &templong, header_buf, sizeof( templong ));
351 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
352 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
356 memcpy(&templong, header_buf + 4, sizeof( templong ));
357 templong = ntohl( templong );
358 if ( templong == AD_VERSION1 ) {
360 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
362 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
366 } else if ( templong == AD_VERSION2 ) {
368 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
371 "Warning: %s may be a corrupt AppleSingle file.\n",
376 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
385 * single_read is called until it returns zero for each fork. When
386 * it returns zero for the first fork, it seeks to the proper place
387 * to read in the next, if there is one. single_read must be called
388 * enough times to return zero for each fork and no more.
392 ssize_t single_read( int fork, char *buffer, size_t length)
402 entry_id = ADEID_DFORK;
405 entry_id = ADEID_RFORK;
412 if (single.entry[entry_id].ade_len > 0x7FFFFFFF) {
413 fprintf(stderr, "single_read: Trying to read past end of fork!, ade_len == %u\n", single.entry[entry_id].ade_len);
416 if ( single.entry[ entry_id ].ade_len == 0 ) {
417 if ( fork == DATA ) {
418 pos = lseek( single.filed,
419 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
424 if ( single.entry[ entry_id ].ade_len < length ) {
425 readlen = single.entry[ entry_id ].ade_len;
431 while (( readlen > 0 ) && ( cc > 0 )) {
432 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
438 cc = buf_ptr - buffer;
439 single.entry[ entry_id ].ade_len -= cc;