loop: fix NULL dereference if mount fails
[safe/jmp/linux-2.6] / fs / cifs / netmisc.c
index b66eff5..bd6d689 100644 (file)
@@ -1,25 +1,24 @@
 /*
  *   fs/cifs/netmisc.c
  *
- *   Copyright (c) International Business Machines  Corp., 2002
+ *   Copyright (c) International Business Machines  Corp., 2002,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
- * 
+ *
  *   Error mapping routines from Samba libsmb/errormap.c
  *   Copyright (C) Andrew Tridgell 2001
  *
- *
  *   This program is free software;  you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or 
+ *   the Free Software Foundation; either version 2 of the License, or
  *   (at your option) any later version.
- * 
+ *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  *   the GNU General Public License for more details.
  *
  *   You should have received a copy of the GNU General Public License
- *   along with this program;  if not, write to the Free Software 
+ *   along with this program;  if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
@@ -30,6 +29,7 @@
 #include <linux/fs.h>
 #include <asm/div64.h>
 #include <asm/byteorder.h>
+#include <linux/inet.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
 #include "cifsglob.h"
@@ -64,21 +64,23 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
        {ERRbadshare, -ETXTBSY},
        {ERRlock, -EACCES},
        {ERRunsup, -EINVAL},
-       {ERRnosuchshare,-ENXIO},
+       {ERRnosuchshare, -ENXIO},
        {ERRfilexists, -EEXIST},
        {ERRinvparm, -EINVAL},
        {ERRdiskfull, -ENOSPC},
        {ERRinvname, -ENOENT},
-       {ERRinvlevel,-EOPNOTSUPP},
+       {ERRinvlevel, -EOPNOTSUPP},
        {ERRdirnotempty, -ENOTEMPTY},
        {ERRnotlocked, -ENOLCK},
+       {ERRcancelviolation, -ENOLCK},
        {ERRalreadyexists, -EEXIST},
        {ERRmoredata, -EOVERFLOW},
-       {ERReasnotsupported,-EOPNOTSUPP},
+       {ERReasnotsupported, -EOPNOTSUPP},
        {ErrQuota, -EDQUOT},
        {ErrNotALink, -ENOLINK},
-       {ERRnetlogonNotStarted,-ENOPROTOOPT},
-       {ErrTooManyLinks,-EMLINK},
+       {ERRnetlogonNotStarted, -ENOPROTOOPT},
+       {ERRsymlink, -EOPNOTSUPP},
+       {ErrTooManyLinks, -EMLINK},
        {0, 0}
 };
 
@@ -113,10 +115,16 @@ static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
        {ERRusempx, -EIO},
        {ERRusestd, -EIO},
        {ERR_NOTIFY_ENUM_DIR, -ENOBUFS},
-       {ERRaccountexpired, -EACCES},
+       {ERRnoSuchUser, -EACCES},
+/*     {ERRaccountexpired, -EACCES},
+       {ERRbadclient, -EACCES},
+       {ERRbadLogonTime, -EACCES},
+       {ERRpasswordExpired, -EACCES},*/
+       {ERRaccountexpired, -EKEYEXPIRED},
        {ERRbadclient, -EACCES},
        {ERRbadLogonTime, -EACCES},
-       {ERRpasswordExpired, -EACCES},
+       {ERRpasswordExpired, -EKEYEXPIRED},
+
        {ERRnosupport, -EINVAL},
        {0, 0}
 };
@@ -125,72 +133,72 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = {
        {0, 0}
 };
 
-/* Convert string containing dotted ip address to binary form */
-/* returns 0 if invalid address */
+/*
+ * Convert a string containing text IPv4 or IPv6 address to binary form.
+ *
+ * Returns 0 on failure.
+ */
+static int
+cifs_inet_pton(const int address_family, const char *cp, void *dst)
+{
+       int ret = 0;
+
+       /* calculate length by finding first slash or NULL */
+       if (address_family == AF_INET)
+               ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL);
+       else if (address_family == AF_INET6)
+               ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL);
+
+       cFYI(DBG2, ("address conversion returned %d for %s", ret, cp));
+       if (ret > 0)
+               ret = 1;
+       return ret;
+}
 
-/* BB add address family, change rc to status flag and return union or for ipv6 */
-/*  will need parent to call something like inet_pton to convert ipv6 address  BB */
+/*
+ * Try to convert a string to an IPv4 address and then attempt to convert
+ * it to an IPv6 address if that fails. Set the family field if either
+ * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to
+ * treat the part following it as a numeric sin6_scope_id.
+ *
+ * Returns 0 on failure.
+ */
 int
-cifs_inet_pton(int address_family, char *cp,void *dst)
+cifs_convert_address(char *src, void *dst)
 {
-       int value;
-       int digit;
-       int i;
-       char temp;
-       char bytes[4];
-       char *end = bytes;
-       static const int addr_class_max[4] =
-           { 0xffffffff, 0xffffff, 0xffff, 0xff };
-
-       if(address_family != AF_INET)
-               return -EAFNOSUPPORT;
-
-       for (i = 0; i < 4; i++) {
-               bytes[i] = 0;
+       int rc;
+       char *pct, *endp;
+       struct sockaddr_in *s4 = (struct sockaddr_in *) dst;
+       struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst;
+
+       /* IPv4 address */
+       if (cifs_inet_pton(AF_INET, src, &s4->sin_addr.s_addr)) {
+               s4->sin_family = AF_INET;
+               return 1;
        }
 
-       temp = *cp;
+       /* temporarily terminate string */
+       pct = strchr(src, '%');
+       if (pct)
+               *pct = '\0';
 
-       while (TRUE) {
-               if (!isdigit(temp))
-                       return 0;
+       rc = cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr);
 
-               value = 0;
-               digit = 0;
-               for (;;) {
-                       if (isascii(temp) && isdigit(temp)) {
-                               value = (value * 10) + temp - '0';
-                               temp = *++cp;
-                               digit = 1;
-                       } else
-                               break;
-               }
+       /* repair temp termination (if any) and make pct point to scopeid */
+       if (pct)
+               *pct++ = '%';
 
-               if (temp == '.') {
-                       if ((end > bytes + 2) || (value > 255))
-                               return 0;
-                       *end++ = value;
-                       temp = *++cp;
-               } else if (temp == ':') {
-                       cFYI(1,("IPv6 addresses not supported for CIFS mounts yet"));
-                       return -1;
-               } else
-                       break;
-       }
+       if (!rc)
+               return rc;
 
-       /* check for last characters */
-       if (temp != '\0' && (!isascii(temp) || !isspace(temp)))
-               if (temp != '\\') {
-                       if (temp != '/')
-                               return 0;
-                       else
-                               (*cp = '\\');   /* switch the slash the expected way */
-               }
-       if (value > addr_class_max[end - bytes])
-               return 0;
+       s6->sin6_family = AF_INET6;
+       if (pct) {
+               s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0);
+               if (!*pct || *endp)
+                       return 0;
+       }
 
-       *((__be32 *)dst) = *((__be32 *) bytes) | htonl(value);
-       return 1; /* success */
+       return rc;
 }
 
 /*****************************************************************************
@@ -225,7 +233,7 @@ static const struct {
        ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, {
        ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
 /*     { This NT error code was 'sqashed'
-        from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK 
+        from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK
         during the session setup } */
        {
        ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, {
@@ -240,7 +248,7 @@ static const struct {
        ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, {
        ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
 /*     { This NT error code was 'sqashed'
-        from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 
+        from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
         during the session setup }   */
        {
        ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, {
@@ -260,7 +268,8 @@ static const struct {
        ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, {
        ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, {
        ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, {
-       ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, {   /* mapping changed since shell does lookup on * and expects file not found */
+        /* mapping changed since shell does lookup on * expects FileNotFound */
+       ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, {
        ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, {
        ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, {
        ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, {
@@ -310,17 +319,17 @@ static const struct {
        ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, {
        ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
 /*     { This NT error code was 'sqashed'
-        from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE 
+        from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE
         during the session setup } */
        {
-       ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, {
+       ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */
        ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, {
        ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, {
        ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, {
        ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, {
        ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
 /*     { This NT error code was 'sqashed'
-        from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE 
+        from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE
         during the session setup } */
        {
        ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, {
@@ -328,10 +337,10 @@ static const struct {
        ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, {
        ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, {
        ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, {
-       ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS}, {
-       ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION}, {
+       ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, {
+       ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, {
        ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, {
-       ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED}, {
+       ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, {
        ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, {
        ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, {
        ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, {
@@ -372,8 +381,8 @@ static const struct {
        ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, {
        ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
 /*     { This NT error code was 'sqashed'
-        from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES 
-        during the session setup } */
+        from NT_STATUS_INSUFFICIENT_RESOURCES to
+        NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */
        {
        ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, {
        ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {
@@ -617,8 +626,8 @@ static const struct {
        ERRDOS, 19, NT_STATUS_TOO_LATE}, {
        ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
 /*     { This NT error code was 'sqashed'
-        from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 
-        during the session setup } */
+        from NT_STATUS_NO_TRUST_SAM_ACCOUNT to
+        NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */
        {
        ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, {
        ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, {
@@ -628,7 +637,7 @@ static const struct {
        ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, {
        ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, {
        ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, {
-       ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED}, {
+       ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, {
        ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, {
        ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, {
        ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, {
@@ -637,7 +646,7 @@ static const struct {
        ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, {
        ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
 /*     { This NT error code was 'sqashed'
-        from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE 
+        from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE
         during the session setup }  */
        {
        ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, {
@@ -754,6 +763,7 @@ static const struct {
        ERRDOS, ERRnoaccess, 0xc000028f}, {
        ERRDOS, ERRnoaccess, 0xc0000290}, {
        ERRDOS, ERRbadfunc, 0xc000029c}, {
+       ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, {
        ERRDOS, ERRinvlevel, 0x007c0001}, };
 
 /*****************************************************************************
@@ -768,7 +778,7 @@ cifs_print_status(__u32 status_code)
                if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) ==
                    (status_code & 0xFFFFFF)) {
                        printk(KERN_NOTICE "Status code returned 0x%08x %s\n",
-                                  status_code,nt_errs[idx].nt_errstr);
+                                  status_code, nt_errs[idx].nt_errstr);
                }
                idx++;
        }
@@ -777,7 +787,7 @@ cifs_print_status(__u32 status_code)
 
 
 static void
-ntstatus_to_dos(__u32 ntstatus, __u8 * eclass, __u16 * ecode)
+ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode)
 {
        int i;
        if (ntstatus == 0) {
@@ -797,10 +807,10 @@ ntstatus_to_dos(__u32 ntstatus, __u8 * eclass, __u16 * ecode)
 }
 
 int
-map_smb_to_linux_error(struct smb_hdr *smb)
+map_smb_to_linux_error(struct smb_hdr *smb, int logErr)
 {
        unsigned int i;
-       int rc = -EIO;          /* if transport error smb error may not be set */
+       int rc = -EIO;  /* if transport error smb error may not be set */
        __u8 smberrclass;
        __u16 smberrcode;
 
@@ -811,9 +821,12 @@ map_smb_to_linux_error(struct smb_hdr *smb)
                return 0;
 
        if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
-               /* translate the newer STATUS codes to old style errors and then to POSIX errors */
+               /* translate the newer STATUS codes to old style SMB errors
+                * and then to POSIX errors */
                __u32 err = le32_to_cpu(smb->Status.CifsError);
-               if(cifsFYI & CIFS_RC)
+               if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED)))
+                       cifs_print_status(err);
+               else if (cifsFYI & CIFS_RC)
                        cifs_print_status(err);
                ntstatus_to_dos(err, &smberrclass, &smberrcode);
        } else {
@@ -824,38 +837,44 @@ map_smb_to_linux_error(struct smb_hdr *smb)
        /* old style errors */
 
        /* DOS class smb error codes - map DOS */
-       if (smberrclass == ERRDOS) {    /* one byte field no need to byte reverse */
+       if (smberrclass == ERRDOS) {
+               /* 1 byte field no need to byte reverse */
                for (i = 0;
                     i <
-                    sizeof (mapping_table_ERRDOS) /
-                    sizeof (struct smb_to_posix_error); i++) {
+                    sizeof(mapping_table_ERRDOS) /
+                    sizeof(struct smb_to_posix_error); i++) {
                        if (mapping_table_ERRDOS[i].smb_err == 0)
                                break;
-                       else if (mapping_table_ERRDOS[i].smb_err == smberrcode) {
+                       else if (mapping_table_ERRDOS[i].smb_err ==
+                                                               smberrcode) {
                                rc = mapping_table_ERRDOS[i].posix_code;
                                break;
                        }
-                       /* else try the next error mapping one to see if it will match */
+                       /* else try next error mapping one to see if match */
                }
-       } else if (smberrclass == ERRSRV) {     /* server class of error codes */
+       } else if (smberrclass == ERRSRV) {
+               /* server class of error codes */
                for (i = 0;
                     i <
-                    sizeof (mapping_table_ERRSRV) /
-                    sizeof (struct smb_to_posix_error); i++) {
+                    sizeof(mapping_table_ERRSRV) /
+                    sizeof(struct smb_to_posix_error); i++) {
                        if (mapping_table_ERRSRV[i].smb_err == 0)
                                break;
-                       else if (mapping_table_ERRSRV[i].smb_err == smberrcode) {
+                       else if (mapping_table_ERRSRV[i].smb_err ==
+                                                               smberrcode) {
                                rc = mapping_table_ERRSRV[i].posix_code;
                                break;
                        }
-                       /* else try the next error mapping one to see if it will match */
+                       /* else try next error mapping to see if match */
                }
        }
        /* else ERRHRD class errors or junk  - return EIO */
 
-       cFYI(1, (" !!Mapping smb error code %d to POSIX err %d !!", smberrcode,rc));
+       cFYI(1, ("Mapping smb error code %d to POSIX err %d",
+                smberrcode, rc));
 
-       /* generic corrective action e.g. reconnect SMB session on ERRbaduid could be added */
+       /* generic corrective action e.g. reconnect SMB session on
+        * ERRbaduid could be added */
 
        return rc;
 }
@@ -867,14 +886,14 @@ map_smb_to_linux_error(struct smb_hdr *smb)
 unsigned int
 smbCalcSize(struct smb_hdr *ptr)
 {
-       return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) +
+       return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
                2 /* size of the bcc field */ + BCC(ptr));
 }
 
 unsigned int
 smbCalcSize_LE(struct smb_hdr *ptr)
 {
-       return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) +
+       return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
                2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr)));
 }
 
@@ -882,22 +901,22 @@ smbCalcSize_LE(struct smb_hdr *ptr)
 
 #define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
 
-    /*
    * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
    * into Unix UTC (based 1970-01-01, in seconds).
    */
+/*
+ * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
+ * into Unix UTC (based 1970-01-01, in seconds).
+ */
 struct timespec
-cifs_NTtimeToUnix(u64 ntutc)
+cifs_NTtimeToUnix(__le64 ntutc)
 {
-       struct timespec ts; 
+       struct timespec ts;
        /* BB what about the timezone? BB */
 
        /* Subtract the NTFS time offset, then convert to 1s intervals. */
        u64 t;
 
-       t = ntutc - NTFS_TIME_OFFSET;
+       t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
        ts.tv_nsec = do_div(t, 10000000) * 100;
-       ts.tv_sec = t; 
+       ts.tv_sec = t;
        return ts;
 }
 
@@ -908,3 +927,60 @@ cifs_UnixTimeToNT(struct timespec t)
        /* Convert to 100ns intervals and then add the NTFS time offset. */
        return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET;
 }
+
+static int total_days_of_prev_months[] =
+{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
+{
+       struct timespec ts;
+       int sec, min, days, month, year;
+       u16 date = le16_to_cpu(le_date);
+       u16 time = le16_to_cpu(le_time);
+       SMB_TIME *st = (SMB_TIME *)&time;
+       SMB_DATE *sd = (SMB_DATE *)&date;
+
+       cFYI(1, ("date %d time %d", date, time));
+
+       sec = 2 * st->TwoSeconds;
+       min = st->Minutes;
+       if ((sec > 59) || (min > 59))
+               cERROR(1, ("illegal time min %d sec %d", min, sec));
+       sec += (min * 60);
+       sec += 60 * 60 * st->Hours;
+       if (st->Hours > 24)
+               cERROR(1, ("illegal hours %d", st->Hours));
+       days = sd->Day;
+       month = sd->Month;
+       if ((days > 31) || (month > 12)) {
+               cERROR(1, ("illegal date, month %d day: %d", month, days));
+               if (month > 12)
+                       month = 12;
+       }
+       month -= 1;
+       days += total_days_of_prev_months[month];
+       days += 3652; /* account for difference in days between 1980 and 1970 */
+       year = sd->Year;
+       days += year * 365;
+       days += (year/4); /* leap year */
+       /* generalized leap year calculation is more complex, ie no leap year
+       for years/100 except for years/400, but since the maximum number for DOS
+        year is 2**7, the last year is 1980+127, which means we need only
+        consider 2 special case years, ie the years 2000 and 2100, and only
+        adjust for the lack of leap year for the year 2100, as 2000 was a
+        leap year (divisable by 400) */
+       if (year >= 120)  /* the year 2100 */
+               days = days - 1;  /* do not count leap year for the year 2100 */
+
+       /* adjust for leap year where we are still before leap day */
+       if (year != 120)
+               days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0);
+       sec += 24 * 60 * 60 * days;
+
+       ts.tv_sec = sec + offset;
+
+       /* cFYI(1,("sec after cnvrt dos to unix time %d",sec)); */
+
+       ts.tv_nsec = 0;
+       return ts;
+}