6 #endif /* HAVE_CONFIG_H */
11 #include <sys/param.h>
19 #include <atalk/adouble.h>
20 #include <netatalk/endian.h>
24 /* String used to indicate standard input instead of a disk
25 file. Should be a string not normally used for a file
36 /* This structure holds an entry description, consisting of three
37 four byte entities. The first is the Entry ID, the second is
38 the File Offset and the third is the Length.
42 /* Both input and output routines use this struct and the
43 following globals; therefore this module can only be used
44 for one of the two functions at a time.
46 static struct single_file_data {
48 char path[ MAXPATHLEN + 1];
49 struct ad_entry entry[ ADEID_MAX ];
52 extern char *forkname[];
53 static u_char header_buf[ AD_HEADER_LEN ];
56 * single_open must be called first. pass it a filename that is supposed
57 * to contain a AppleSingle file. an single struct will be allocated and
58 * somewhat initialized; single_filed is set.
61 int single_open(char *singlefile, int flags, struct FHeader *fh, int options _U_)
65 if ( flags == O_RDONLY ) {
66 if ( strcmp( singlefile, STDIN ) == 0 ) {
67 single.filed = fileno( stdin );
68 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
72 strncpy( single.path, singlefile, MAXPATHLEN );
74 fprintf( stderr, "opened %s for read\n", single.path );
76 if ((( rc = single_header_test()) > 0 ) &&
77 ( single_header_read( fh, rc ) == 0 )) {
87 * single_close must be called before a second file can be opened using
88 * single_open. Upon successful completion, a value of 0 is returned.
89 * Otherwise, a value of -1 is returned.
92 int single_close( int keepflag)
94 if ( keepflag == KEEP ) {
95 return( close( single.filed ));
96 } else if ( keepflag == TRASH ) {
97 if (( strcmp( single.path, STDIN ) != 0 ) &&
98 ( unlink( single.path ) < 0 )) {
99 perror ( single.path );
106 * single_header_read is called by single_open, and before any information
107 * can read from the fh substruct. it must be called before any of the
108 * bytes of the other two forks can be read, as well.
111 int single_header_read( struct FHeader *fh, int version)
114 * entry_buf is used for reading in entry descriptors, and for reading in
115 * the actual entries of FILEINFO, FINDERINFO, and DATES.
117 u_char entry_buf[ADEDLEN_FINDERI];
119 u_int32_t time_seconds;
120 u_short mask = 0xfcee;
128 * Go through and initialize the array of entry_info structs. Read in the
129 * number of entries, and then read in the info for each entry and save it
133 for ( n = 0 ; n < ADEID_MAX; n++ ) {
134 single.entry[ n ].ade_off = 0;
135 single.entry[ n ].ade_len = 0;
137 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
138 num_entries = ntohs( num_entries );
140 fprintf( stderr, "The number of entries is %d\n", num_entries );
142 for ( ; num_entries > 0 ; num_entries-- ) {
143 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
145 perror( "Premature end of file :" );
148 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
149 entry_id = ntohl( entry_id );
150 memcpy(&single.entry[ entry_id ].ade_off,
152 sizeof( single.entry[ entry_id ].ade_off ));
153 single.entry[ entry_id ].ade_off =
154 ntohl( single.entry[ entry_id ].ade_off );
155 memcpy(&single.entry[ entry_id ].ade_len,
157 sizeof( single.entry[ entry_id ].ade_len ));
158 single.entry[ entry_id ].ade_len =
159 ntohl( single.entry[ entry_id ].ade_len );
161 fprintf( stderr, "entry_id\t%d\n", entry_id );
162 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
163 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
168 * Now that the entries have been identified, check to make sure
169 * it is a Macintosh file if dealing with version two format file.
172 if ( version == 1 ) {
173 if ( single.entry[ ADEID_FILEI ].ade_len > 0 )
174 date_entry = ADEID_FILEI;
175 } else if ( version == 2 ) {
176 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 )
177 date_entry = ADEID_FILEDATESI;
180 fprintf( stderr, "date_entry = %d\n", date_entry );
184 * Go through and copy all the information you can get from
185 * the informational entries into the fh struct. The ENTRYID_DATA
186 * must be the last one done, because it leaves the file pointer in
187 * the right place for the first read of the data fork.
190 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
191 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
194 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
196 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
197 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
198 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
199 perror( "Premature end of file :" );
203 if (( single.entry[ ADEID_FINDERI ].ade_len < ADEDLEN_FINDERI ) ||
204 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
205 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
208 pos = lseek( single.filed,
209 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
210 if ( read( single.filed, (char *)entry_buf, ADEDLEN_FINDERI) != ADEDLEN_FINDERI) {
211 perror( "Premature end of file :" );
214 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
215 sizeof( fh->finder_info.fdType ));
216 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
217 sizeof( fh->finder_info.fdCreator ));
218 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
219 sizeof( fh->finder_info.fdFlags ));
220 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
221 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
222 sizeof( fh->finder_info.fdLocation ));
223 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
224 sizeof( fh->finder_info.fdFldr ));
225 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
226 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
233 strncpy( type, &fh->finder_info.fdType, 4 );
234 strncpy( creator, &fh->finder_info.fdCreator, 4 );
235 type[4] = creator[4] = '\0';
236 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
240 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
241 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
242 fh->comment[0] = '\0';
244 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
246 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
247 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
248 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
249 perror( "Premature end of file :" );
254 * If date_entry is 7, we have an AppleSingle version one, do the
255 * appropriate stuff. If it is 8, we have an AppleSingle version two,
256 * do the right thing. If date_entry is neither, just use the current date.
257 * Unless I can't get the current date, in which case use time zero.
259 if (( date_entry < 7 ) || ( date_entry > 8 )) {
260 if (( time_seconds = time( NULL )) == (u_int32_t)-1 ) {
261 time_seconds = AD_DATE_START;
263 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
265 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
266 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
267 fh->backup_date = AD_DATE_START;
268 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
269 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
272 } else if ( date_entry == ADEID_FILEI ) {
273 pos = lseek( single.filed,
274 single.entry[ date_entry ].ade_off, SEEK_SET );
275 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
276 sizeof( entry_buf )) {
277 perror( "Premature end of file :" );
280 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
281 sizeof( fh->create_date ));
282 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
283 sizeof( fh->mod_date ));
284 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
285 sizeof(fh->backup_date));
286 } else if ( date_entry == ADEID_FILEDATESI ) {
287 pos = lseek( single.filed,
288 single.entry[ date_entry ].ade_off, SEEK_SET );
289 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
290 sizeof( entry_buf )) {
291 perror( "Premature end of file :" );
294 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
295 sizeof( fh->create_date ));
296 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
297 sizeof( fh->mod_date ));
298 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
299 sizeof(fh->backup_date));
301 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
302 fh->forklen[RESOURCE] = 0;
304 fh->forklen[RESOURCE] =
305 htonl( single.entry[ ADEID_RFORK ].ade_len );
307 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
308 fh->forklen[ DATA ] = 0;
310 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
311 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
318 * single_header_test is called from single_open. It checks certain
319 * values of the file and determines if the file is an AppleSingle version
320 * one file something else, and returns a one, or negative one to indicate
323 * The Magic Number of the file, the first four bytes, must be hex
324 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
325 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
326 * are only interested in files from Macs. Therefore these bytes must
327 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
328 * "Macintosh " (that is seven blanks of padding).
330 #define MACINTOSH "Macintosh "
331 static u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
332 0, 0, 0, 0, 0, 0, 0, 0 };
334 int single_header_test(void)
339 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
340 if ( cc < (ssize_t)sizeof( header_buf )) {
341 perror( "Premature end of file :" );
345 memcpy( &templong, header_buf, sizeof( templong ));
346 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
347 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
351 memcpy(&templong, header_buf + 4, sizeof( templong ));
352 templong = ntohl( templong );
353 if ( templong == AD_VERSION1 ) {
355 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
357 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
361 } else if ( templong == AD_VERSION2 ) {
363 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
366 "Warning: %s may be a corrupt AppleSingle file.\n",
371 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
380 * single_read is called until it returns zero for each fork. When
381 * it returns zero for the first fork, it seeks to the proper place
382 * to read in the next, if there is one. single_read must be called
383 * enough times to return zero for each fork and no more.
387 ssize_t single_read( int fork, char *buffer, size_t length)
397 entry_id = ADEID_DFORK;
400 entry_id = ADEID_RFORK;
407 if (single.entry[entry_id].ade_len > 0x7FFFFFFF) {
408 fprintf(stderr, "single_read: Trying to read past end of fork!, ade_len == %u\n", single.entry[entry_id].ade_len);
411 if ( single.entry[ entry_id ].ade_len == 0 ) {
412 if ( fork == DATA ) {
413 pos = lseek( single.filed,
414 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
419 if ( single.entry[ entry_id ].ade_len < length ) {
420 readlen = single.entry[ entry_id ].ade_len;
426 while (( readlen > 0 ) && ( cc > 0 )) {
427 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
433 cc = buf_ptr - buffer;
434 single.entry[ entry_id ].ade_len -= cc;