1/*  $OpenBSD: msdosfs_conv.c,v 1.22 2024/09/12 09:07:28 claudio Exp $   */
2/*  $NetBSD: msdosfs_conv.c,v 1.24 1997/10/17 11:23:54 ws Exp $ */
3
4/*-
5 * Copyright (C) 1995, 1997 Wolfgang Solfrank.
6 * Copyright (C) 1995, 1997 TooLs GmbH.
7 * All rights reserved.
8 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *  This product includes software developed by TooLs GmbH.
21 * 4. The name of TooLs GmbH may not be used to endorse or promote products
22 *    derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35/*
36 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 *
38 * You can do anything you want with this software, just don't say you wrote
39 * it, and don't remove this notice.
40 *
41 * This software is provided "as is".
42 *
43 * The author supplies this software to be publicly redistributed on the
44 * understanding that the author is not responsible for the correct
45 * functioning of this software in any circumstances and is not liable for
46 * any damages caused by this software.
47 *
48 * October 1992
49 */
50
51/*
52 * System include files.
53 */
54#include <sys/param.h>
55#include <sys/systm.h>
56#include <sys/time.h>
57#include <sys/kernel.h>     /* defines tz */
58#include <sys/dirent.h>
59#include <sys/vnode.h>
60#include <sys/lock.h>
61
62/*
63 * MSDOSFS include files.
64 */
65#include <msdosfs/direntry.h>
66#include <msdosfs/denode.h>
67
68/*
69 * Days in each month in a regular year.
70 */
71const u_short regyear[] = {
72    31, 28, 31, 30, 31, 30,
73    31, 31, 30, 31, 30, 31
74};
75
76/*
77 * Days in each month in a leap year.
78 */
79const u_short leapyear[] = {
80    31, 29, 31, 30, 31, 30,
81    31, 31, 30, 31, 30, 31
82};
83
84/*
85 * Variables used to remember parts of the last time conversion.  Maybe we
86 * can avoid a full conversion.
87 */
88time_t lasttime;
89uint32_t lastday;
90u_short lastddate;
91u_short lastdtime;
92
93/*
94 * Convert the unix version of time to dos's idea of time to be used in
95 * file timestamps. The passed in unix time is assumed to be in GMT.
96 */
97void
98unix2dostime(struct timespec *tsp, u_int16_t *ddp, u_int16_t *dtp, u_int8_t *dhp)
99{
100    time_t t;
101    uint32_t days;
102    uint32_t inc;
103    uint32_t year;
104    uint32_t month;
105    const u_short *months;
106
107    /*
108     * If the time from the last conversion is the same as now, then
109     * skip the computations and use the saved result.
110     */
111    t = tsp->tv_sec;
112    t &= ~1;
113    /*
114     * Before 1/1/1980 there is only a timeless void. After 12/31/2107
115     * there is only Cthulhu.
116     */
117#define DOSEPOCH 315532800LL
118#define DOSENDTIME 4354775999LL
119    if (t < DOSEPOCH || t > DOSENDTIME)
120        t = DOSEPOCH;
121
122    if (lasttime != t) {
123        lasttime = t;
124        lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT)
125            + (((t / 60) % 60) << DT_MINUTES_SHIFT)
126            + (((t / 3600) % 24) << DT_HOURS_SHIFT);
127
128        /*
129         * If the number of days since 1970 is the same as the last
130         * time we did the computation then skip all this leap year
131         * and month stuff.
132         */
133        days = t / (24 * 60 * 60);
134        if (days != lastday) {
135            lastday = days;
136            for (year = 1970;; year++) {
137                inc = year & 0x03 ? 365 : 366;
138                if (days < inc)
139                    break;
140                days -= inc;
141            }
142            months = year & 0x03 ? regyear : leapyear;
143            for (month = 0; month < 12; month++) {
144                if (days < months[month])
145                    break;
146                days -= months[month];
147            }
148            lastddate = ((days + 1) << DD_DAY_SHIFT)
149                + ((month + 1) << DD_MONTH_SHIFT);
150            /*
151             * Remember dos's idea of time is relative to 1980.
152             * unix's is relative to 1970.  If somehow we get a
153             * time before 1980 then don't give totally crazy
154             * results.
155             */
156            if (year > 1980)
157                lastddate += (year - 1980) << DD_YEAR_SHIFT;
158        }
159    }
160
161    if (dtp != NULL)
162        *dtp = lastdtime;
163    if (dhp != NULL)
164            *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
165
166    *ddp = lastddate;
167}
168
169/*
170 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
171 * interval there were 8 regular years and 2 leap years.
172 */
173#define SECONDSTO1980   (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
174
175u_short lastdosdate;
176uint32_t lastseconds;
177
178/*
179 * Convert from dos' idea of time to unix'. This will probably only be
180 * called from the stat(), and fstat() system calls and so probably need
181 * not be too efficient.
182 */
183void
184dos2unixtime(u_int dd, u_int dt, u_int dh, struct timespec *tsp)
185{
186    uint32_t seconds;
187    uint32_t m, month;
188    uint32_t y, year;
189    uint32_t days;
190    const u_short *months;
191
192    if (dd == 0) {
193        /*
194         * Uninitialized field, return the epoch.
195         */
196        tsp->tv_sec = 0;
197        tsp->tv_nsec = 0;
198        return;
199    }
200    seconds = ((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) * 2
201        + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
202        + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600
203        + dh / 100;
204    /*
205     * If the year, month, and day from the last conversion are the
206     * same then use the saved value.
207     */
208    if (lastdosdate != dd) {
209        lastdosdate = dd;
210        days = 0;
211        year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
212        for (y = 0; y < year; y++)
213            days += y & 0x03 ? 365 : 366;
214        months = year & 0x03 ? regyear : leapyear;
215        /*
216         * Prevent going from 0 to 0xffffffff in the following
217         * loop.
218         */
219        month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
220        if (month == 0) {
221            printf("dos2unixtime(): month value out of range (%u)\n",
222                month);
223            month = 1;
224        }
225        for (m = 0; m < month - 1; m++)
226            days += months[m];
227        days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
228        lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
229    }
230    tsp->tv_sec = seconds + lastseconds;
231    tsp->tv_nsec = (dh % 100) * 10000000;
232}
233
234static const u_char
235unix2dos[256] = {
236    0,    0,    0,    0,    0,    0,    0,    0,    /* 00-07 */
237    0,    0,    0,    0,    0,    0,    0,    0,    /* 08-0f */
238    0,    0,    0,    0,    0,    0,    0,    0,    /* 10-17 */
239    0,    0,    0,    0,    0,    0,    0,    0,    /* 18-1f */
240    0,    0x21, 0,    0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */
241    0x28, 0x29, 0,    0,    0,    0x2d, 0,    0,    /* 28-2f */
242    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */
243    0x38, 0x39, 0,    0,    0,    0,    0,    0,    /* 38-3f */
244    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */
245    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */
246    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */
247    0x58, 0x59, 0x5a, 0,    0,    0,    0x5e, 0x5f, /* 58-5f */
248    0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 60-67 */
249    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 68-6f */
250    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 70-77 */
251    0x58, 0x59, 0x5a, 0x7b, 0,    0x7d, 0x7e, 0,    /* 78-7f */
252    0,    0,    0,    0,    0,    0,    0,    0,    /* 80-87 */
253    0,    0,    0,    0,    0,    0,    0,    0,    /* 88-8f */
254    0,    0,    0,    0,    0,    0,    0,    0,    /* 90-97 */
255    0,    0,    0,    0,    0,    0,    0,    0,    /* 98-9f */
256    0,    0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */
257    0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */
258    0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */
259    0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */
260    0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */
261    0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */
262    0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */
263    0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */
264    0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */
265    0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */
266    0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */
267    0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */
268};
269
270static const u_char
271dos2unix[256] = {
272    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 00-07 */
273    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 08-0f */
274    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 10-17 */
275    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 18-1f */
276    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */
277    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x3f, /* 28-2f */
278    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */
279    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */
280    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */
281    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */
282    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */
283    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */
284    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */
285    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */
286    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */
287    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */
288    0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */
289    0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8f */
290    0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */
291    0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, 0x3f, /* 98-9f */
292    0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* a0-a7 */
293    0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* a8-af */
294    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xc1, 0xc2, 0xc0, /* b0-b7 */
295    0xa9, 0x3f, 0x3f, 0x3f, 0x3f, 0xa2, 0xa5, 0x3f, /* b8-bf */
296    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xe3, 0xc3, /* c0-c7 */
297    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xa4, /* c8-cf */
298    0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0x3f, 0xcd, 0xce, /* d0-d7 */
299    0xcf, 0x3f, 0x3f, 0x3f, 0x3f, 0xa6, 0xcc, 0x3f, /* d8-df */
300    0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe, /* e0-e7 */
301    0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0x3f, /* e8-ef */
302    0xad, 0xb1, 0x3f, 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, /* f0-f7 */
303    0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, 0x3f, 0x3f, /* f8-ff */
304};
305
306static const u_char
307u2l[256] = {
308    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */
309    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */
310    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */
311    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */
312    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */
313    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x3f, /* 28-2f */
314    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */
315    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */
316    0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */
317    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */
318    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */
319    0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */
320    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */
321    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */
322    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */
323    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */
324    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */
325    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */
326    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */
327    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */
328    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */
329    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */
330    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */
331    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */
332    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */
333    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */
334    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */
335    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */
336    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */
337    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */
338    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */
339    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */
340};
341
342/*
343 * DOS filenames are made of 2 parts, the name part and the extension part.
344 * The name part is 8 characters long and the extension part is 3
345 * characters long.  They may contain trailing blanks if the name or
346 * extension are not long enough to fill their respective fields.
347 */
348
349/*
350 * Convert a DOS filename to a unix filename. And, return the number of
351 * characters in the resulting unix filename excluding the terminating
352 * null.
353 */
354int
355dos2unixfn(u_char dn[11], u_char *un, int lower)
356{
357    int i;
358    int thislong = 1;
359    u_char c;
360
361    /*
362     * If first char of the filename is SLOT_E5 (0x05), then the real
363     * first char of the filename should be 0xe5. But, they couldn't
364     * just have a 0xe5 mean 0xe5 because that is used to mean a freed
365     * directory slot. Another dos quirk.
366     */
367    if (*dn == SLOT_E5)
368        c = dos2unix[0xe5];
369    else
370        c = dos2unix[*dn];
371    *un++ = lower ? u2l[c] : c;
372    dn++;
373
374    /*
375     * Copy the name portion into the unix filename string.
376     */
377    for (i = 1; i < 8 && *dn != ' '; i++) {
378        c = dos2unix[*dn++];
379        *un++ = lower ? u2l[c] : c;
380        thislong++;
381    }
382    dn += 8 - i;
383
384    /*
385     * Now, if there is an extension then put in a period and copy in
386     * the extension.
387     */
388    if (*dn != ' ') {
389        *un++ = '.';
390        thislong++;
391        for (i = 0; i < 3 && *dn != ' '; i++) {
392            c = dos2unix[*dn++];
393            *un++ = lower ? u2l[c] : c;
394            thislong++;
395        }
396    }
397    *un++ = 0;
398
399    return (thislong);
400}
401
402/*
403 * Convert a unix filename to a DOS filename according to Win95 rules.
404 * If applicable and gen is not 0, it is inserted into the converted
405 * filename as a generation number.
406 * Returns
407 *  0 if name couldn't be converted
408 *  1 if the converted name is the same as the original
409 *    (no long filename entry necessary for Win95)
410 *  2 if conversion was successful
411 *  3 if conversion was successful and generation number was inserted
412 */
413int
414unix2dosfn(u_char *un, u_char dn[11], int unlen, u_int gen)
415{
416    int i, j, l;
417    int conv = 1;
418    u_char *cp, *dp, *dp1;
419    u_char gentext[6];
420
421    /*
422     * Fill the dos filename string with blanks. These are DOS's pad
423     * characters.
424     */
425    for (i = 0; i < 11; i++)
426        dn[i] = ' ';
427
428    /*
429     * The filenames "." and ".." are handled specially, since they
430     * don't follow dos filename rules.
431     */
432    if (un[0] == '.' && unlen == 1) {
433        dn[0] = '.';
434        return gen <= 1;
435    }
436    if (un[0] == '.' && un[1] == '.' && unlen == 2) {
437        dn[0] = '.';
438        dn[1] = '.';
439        return gen <= 1;
440    }
441
442    /*
443     * Filenames with only blanks and dots are not allowed!
444     */
445    for (cp = un, i = unlen; --i >= 0; cp++)
446        if (*cp != ' ' && *cp != '.')
447            break;
448    if (i < 0)
449        return 0;
450
451    /*
452     * Now find the extension
453     * Note: dot as first char doesn't start extension
454     *   and trailing dots and blanks are ignored
455     */
456    dp = dp1 = 0;
457    for (cp = un + 1, i = unlen - 1; --i >= 0;) {
458        switch (*cp++) {
459        case '.':
460            if (!dp1)
461                dp1 = cp;
462            break;
463        case ' ':
464            break;
465        default:
466            if (dp1)
467                dp = dp1;
468            dp1 = 0;
469            break;
470        }
471    }
472
473    /*
474     * Now convert it
475     */
476    if (dp) {
477        if (dp1)
478            l = dp1 - dp;
479        else
480            l = unlen - (dp - un);
481        for (i = 0, j = 8; i < l && j < 11; i++, j++) {
482            if (dp[i] != (dn[j] = unix2dos[dp[i]])
483                && conv != 3)
484                conv = 2;
485            if (!dn[j]) {
486                conv = 3;
487                dn[j--] = ' ';
488            }
489        }
490        if (i < l)
491            conv = 3;
492        dp--;
493    } else {
494        for (dp = cp; *--dp == ' ' || *dp == '.';)
495            ;
496        dp++;
497    }
498
499    /*
500     * Now convert the rest of the name
501     */
502    for (i = j = 0; un < dp && j < 8; i++, j++, un++) {
503        if (*un != (dn[j] = unix2dos[*un])
504            && conv != 3)
505            conv = 2;
506        if (!dn[j]) {
507            conv = 3;
508            dn[j--] = ' ';
509        }
510    }
511    if (un < dp)
512        conv = 3;
513    /*
514     * If we didn't have any chars in filename,
515     * generate a default
516     */
517    if (!j)
518        dn[0] = '_';
519
520    /*
521     * The first character cannot be E5,
522     * because that means a deleted entry
523     */
524    if (dn[0] == 0xe5)
525        dn[0] = SLOT_E5;
526
527    /*
528     * If there wasn't any char dropped,
529     * there is no place for generation numbers
530     */
531    if (conv != 3) {
532        if (gen > 1)
533            return 0;
534        return conv;
535    }
536
537    /*
538     * Now insert the generation number into the filename part
539     */
540    for (cp = gentext + sizeof(gentext); cp > gentext && gen; gen /= 10)
541        *--cp = gen % 10 + '0';
542    if (gen)
543        return 0;
544    for (i = 8; dn[--i] == ' ';)
545        ;
546    if (gentext + sizeof(gentext) - cp + 1 > 8 - i)
547        i = 8 - (gentext + sizeof(gentext) - cp + 1);
548    dn[i++] = '~';
549    while (cp < gentext + sizeof(gentext))
550        dn[i++] = *cp++;
551    return 3;
552}
553
554/*
555 * Create a Win95 long name directory entry
556 * Note: assumes that the filename is valid,
557 *   i.e. doesn't consist solely of blanks and dots
558 */
559int
560unix2winfn(u_char *un, int unlen, struct winentry *wep, int cnt, int chksum)
561{
562    u_int8_t *cp;
563    int i;
564
565    /*
566     * Drop trailing blanks and dots
567     */
568    for (cp = un + unlen; *--cp == ' ' || *cp == '.'; unlen--)
569        ;
570
571    un += (cnt - 1) * WIN_CHARS;
572    unlen -= (cnt - 1) * WIN_CHARS;
573
574    /*
575     * Initialize winentry to some useful default
576     */
577    for (cp = (u_int8_t *)wep, i = sizeof(*wep); --i >= 0; *cp++ = 0xff)
578        ;
579    wep->weCnt = cnt;
580    wep->weAttributes = ATTR_WIN95;
581    wep->weReserved1 = 0;
582    wep->weChksum = chksum;
583    wep->weReserved2 = 0;
584
585    /*
586     * Now convert the filename parts
587     */
588    for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) {
589        if (--unlen < 0)
590            goto done;
591        *cp++ = *un++;
592        *cp++ = 0;
593    }
594    for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) {
595        if (--unlen < 0)
596            goto done;
597        *cp++ = *un++;
598        *cp++ = 0;
599    }
600    for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) {
601        if (--unlen < 0)
602            goto done;
603        *cp++ = *un++;
604        *cp++ = 0;
605    }
606    if (!unlen)
607        wep->weCnt |= WIN_LAST;
608    return unlen;
609
610done:
611    *cp++ = 0;
612    *cp++ = 0;
613    wep->weCnt |= WIN_LAST;
614    return 0;
615}
616
617/*
618 * Compare our filename to the one in the Win95 entry
619 * Returns the checksum or -1 if no match
620 */
621int
622winChkName(u_char *un, int unlen, struct winentry *wep, int chksum)
623{
624    u_int8_t *cp;
625    int i;
626
627    /*
628     * First compare checksums
629     */
630    if (wep->weCnt&WIN_LAST)
631        chksum = wep->weChksum;
632    else if (chksum != wep->weChksum)
633        chksum = -1;
634    if (chksum == -1)
635        return -1;
636
637    /*
638     * Offset of this entry
639     */
640    i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS;
641    if ((unlen -= i) <= 0)
642        return -1;
643    un += i;
644
645    if ((wep->weCnt&WIN_LAST) && unlen > WIN_CHARS)
646        return -1;
647
648    /*
649     * Compare the name parts
650     */
651    for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) {
652        if (--unlen < 0) {
653            if (!*cp++ && !*cp)
654                return chksum;
655            return -1;
656        }
657        if (u2l[*cp++] != u2l[*un++] || *cp++)
658            return -1;
659    }
660    for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) {
661        if (--unlen < 0) {
662            if (!*cp++ && !*cp)
663                return chksum;
664            return -1;
665        }
666        if (u2l[*cp++] != u2l[*un++] || *cp++)
667            return -1;
668    }
669    for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) {
670        if (--unlen < 0) {
671            if (!*cp++ && !*cp)
672                return chksum;
673            return -1;
674        }
675        if (u2l[*cp++] != u2l[*un++] || *cp++)
676            return -1;
677    }
678    return chksum;
679}
680
681/*
682 * Convert Win95 filename to dirbuf.
683 * Returns the checksum or -1 if impossible
684 */
685int
686win2unixfn(struct winentry *wep, struct dirent *dp, int chksum)
687{
688    u_int8_t *cp;
689    u_int8_t *np, *ep = dp->d_name + WIN_MAXLEN;
690    int i;
691
692    if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS)
693        || !(wep->weCnt&WIN_CNT))
694        return -1;
695
696    /*
697     * First compare checksums
698     */
699    if (wep->weCnt&WIN_LAST) {
700        chksum = wep->weChksum;
701        /*
702         * This works even though d_namlen is one byte!
703         */
704        dp->d_namlen = (wep->weCnt&WIN_CNT) * WIN_CHARS;
705    } else if (chksum != wep->weChksum)
706        chksum = -1;
707    if (chksum == -1)
708        return -1;
709
710    /*
711     * Offset of this entry
712     */
713    i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS;
714    np = (u_int8_t *)dp->d_name + i;
715
716    /*
717     * Convert the name parts
718     */
719    for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) {
720        switch (*np++ = *cp++) {
721        case 0:
722            dp->d_namlen -= sizeof(wep->wePart2)/2
723                + sizeof(wep->wePart3)/2 + i + 1;
724            return chksum;
725        case '/':
726            np[-1] = 0;
727            return -1;
728        }
729        /*
730         * The size comparison should result in the compiler
731         * optimizing the whole if away
732         */
733        if (WIN_MAXLEN % WIN_CHARS < sizeof(wep->wePart1) / 2
734            && np > ep) {
735            np[-1] = 0;
736            return -1;
737        }
738        if (*cp++)
739            return -1;
740    }
741    for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) {
742        switch (*np++ = *cp++) {
743        case 0:
744            dp->d_namlen -= sizeof(wep->wePart3)/2 + i + 1;
745            return chksum;
746        case '/':
747            np[-1] = 0;
748            return -1;
749        }
750        /*
751         * The size comparisons should be optimized away
752         */
753        if (WIN_MAXLEN % WIN_CHARS >= sizeof(wep->wePart1) / 2
754            && WIN_MAXLEN % WIN_CHARS < (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2
755            && np > ep) {
756            np[-1] = 0;
757            return -1;
758        }
759        if (*cp++)
760            return -1;
761    }
762    for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) {
763        switch (*np++ = *cp++) {
764        case 0:
765            dp->d_namlen -= i + 1;
766            return chksum;
767        case '/':
768            np[-1] = 0;
769            return -1;
770        }
771        /*
772         * See above
773         */
774        if (WIN_MAXLEN % WIN_CHARS >= (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2
775            && np > ep) {
776            np[-1] = 0;
777            return -1;
778        }
779        if (*cp++)
780            return -1;
781    }
782    return chksum;
783}
784
785/*
786 * Compute the checksum of a DOS filename for Win95 use
787 */
788u_int8_t
789winChksum(u_int8_t *name)
790{
791    int i;
792    u_int8_t s;
793
794    for (s = 0, i = 11; --i >= 0; s += *name++)
795        s = (s << 7)|(s >> 1);
796    return s;
797}
798
799/*
800 * Determine the number of slots necessary for Win95 names
801 */
802int
803winSlotCnt(u_char *un, int unlen)
804{
805    for (un += unlen; unlen > 0; unlen--)
806        if (*--un != ' ' && *un != '.')
807            break;
808    if (unlen > WIN_MAXLEN)
809        return 0;
810    return howmany(unlen, WIN_CHARS);
811}
812