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[ADEDLEN_FINDERI];
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 < ADEDLEN_FINDERI ) ||
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, ADEDLEN_FINDERI) != ADEDLEN_FINDERI) {
215 perror( "Premature end of file :" );
218 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
219 sizeof( fh->finder_info.fdType ));
220 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
221 sizeof( fh->finder_info.fdCreator ));
222 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
223 sizeof( fh->finder_info.fdFlags ));
224 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
225 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
226 sizeof( fh->finder_info.fdLocation ));
227 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
228 sizeof( fh->finder_info.fdFldr ));
229 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
230 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
237 strncpy( type, &fh->finder_info.fdType, 4 );
238 strncpy( creator, &fh->finder_info.fdCreator, 4 );
239 type[4] = creator[4] = '\0';
240 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
244 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
245 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
246 fh->comment[0] = '\0';
248 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
250 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
251 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
252 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
253 perror( "Premature end of file :" );
258 * If date_entry is 7, we have an AppleSingle version one, do the
259 * appropriate stuff. If it is 8, we have an AppleSingle version two,
260 * do the right thing. If date_entry is neither, just use the current date.
261 * Unless I can't get the current date, in which case use time zero.
263 if (( date_entry < 7 ) || ( date_entry > 8 )) {
264 if (( time_seconds = time( NULL )) == (u_int32_t)-1 ) {
265 time_seconds = AD_DATE_START;
267 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
269 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
270 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
271 fh->backup_date = AD_DATE_START;
272 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
273 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
276 } else if ( date_entry == ADEID_FILEI ) {
277 pos = lseek( single.filed,
278 single.entry[ date_entry ].ade_off, SEEK_SET );
279 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
280 sizeof( entry_buf )) {
281 perror( "Premature end of file :" );
284 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
285 sizeof( fh->create_date ));
286 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
287 sizeof( fh->mod_date ));
288 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
289 sizeof(fh->backup_date));
290 } else if ( date_entry == ADEID_FILEDATESI ) {
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));
305 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
306 fh->forklen[RESOURCE] = 0;
308 fh->forklen[RESOURCE] =
309 htonl( single.entry[ ADEID_RFORK ].ade_len );
311 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
312 fh->forklen[ DATA ] = 0;
314 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
315 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
322 * single_header_test is called from single_open. It checks certain
323 * values of the file and determines if the file is an AppleSingle version
324 * one file something else, and returns a one, or negative one to indicate
327 * The Magic Number of the file, the first four bytes, must be hex
328 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
329 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
330 * are only interested in files from Macs. Therefore these bytes must
331 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
332 * "Macintosh " (that is seven blanks of padding).
334 #define MACINTOSH "Macintosh "
335 static u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
336 0, 0, 0, 0, 0, 0, 0, 0 };
338 int single_header_test(void)
343 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
344 if ( cc < (ssize_t)sizeof( header_buf )) {
345 perror( "Premature end of file :" );
349 memcpy( &templong, header_buf, sizeof( templong ));
350 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
351 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
355 memcpy(&templong, header_buf + 4, sizeof( templong ));
356 templong = ntohl( templong );
357 if ( templong == AD_VERSION1 ) {
359 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
361 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
365 } else if ( templong == AD_VERSION2 ) {
367 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
370 "Warning: %s may be a corrupt AppleSingle file.\n",
375 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
384 * single_read is called until it returns zero for each fork. When
385 * it returns zero for the first fork, it seeks to the proper place
386 * to read in the next, if there is one. single_read must be called
387 * enough times to return zero for each fork and no more.
391 ssize_t single_read( int fork, char *buffer, size_t length)
401 entry_id = ADEID_DFORK;
404 entry_id = ADEID_RFORK;
411 if (single.entry[entry_id].ade_len > 0x7FFFFFFF) {
412 fprintf(stderr, "single_read: Trying to read past end of fork!, ade_len == %u\n", single.entry[entry_id].ade_len);
415 if ( single.entry[ entry_id ].ade_len == 0 ) {
416 if ( fork == DATA ) {
417 pos = lseek( single.filed,
418 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
423 if ( single.entry[ entry_id ].ade_len < length ) {
424 readlen = single.entry[ entry_id ].ade_len;
430 while (( readlen > 0 ) && ( cc > 0 )) {
431 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
437 cc = buf_ptr - buffer;
438 single.entry[ entry_id ].ade_len -= cc;