mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-05-09 21:42:09 +02:00
6fb4e46d2f
Split a variety of bits from netmisc.c into other places, notably the error table into smb1maperror.c. Signed-off-by: David Howells <dhowells@redhat.com> cc: Steve French <sfrench@samba.org> cc: Paulo Alcantara <pc@manguebit.org> cc: Enzo Matsumiya <ematsumiya@suse.de> cc: linux-cifs@vger.kernel.org cc: linux-fsdevel@vger.kernel.org cc: linux-kernel@vger.kernel.org Acked-by: Enzo Matsumiya <ematsumiya@suse.de> Signed-off-by: Steve French <stfrench@microsoft.com>
213 lines
5.6 KiB
C
213 lines
5.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include <linux/net.h>
|
|
#include <linux/string.h>
|
|
#include <linux/in.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/fs.h>
|
|
#include <asm/div64.h>
|
|
#include <asm/byteorder.h>
|
|
#include <linux/inet.h>
|
|
#include "cifsfs.h"
|
|
#include "cifsglob.h"
|
|
#include "cifsproto.h"
|
|
#include "smb1proto.h"
|
|
#include "smberr.h"
|
|
#include "cifs_debug.h"
|
|
#include "nterr.h"
|
|
|
|
/*
|
|
* 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, int len, void *dst)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* calculate length by finding first slash or NULL */
|
|
if (address_family == AF_INET)
|
|
ret = in4_pton(cp, len, dst, '\\', NULL);
|
|
else if (address_family == AF_INET6)
|
|
ret = in6_pton(cp, len, dst , '\\', NULL);
|
|
|
|
cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n",
|
|
ret, len, len, cp);
|
|
if (ret > 0)
|
|
ret = 1;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* 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_convert_address(struct sockaddr *dst, const char *src, int len)
|
|
{
|
|
int rc, alen, slen;
|
|
const char *pct;
|
|
char scope_id[13];
|
|
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, len, &s4->sin_addr.s_addr)) {
|
|
s4->sin_family = AF_INET;
|
|
return 1;
|
|
}
|
|
|
|
/* attempt to exclude the scope ID from the address part */
|
|
pct = memchr(src, '%', len);
|
|
alen = pct ? pct - src : len;
|
|
|
|
rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr);
|
|
if (!rc)
|
|
return rc;
|
|
|
|
s6->sin6_family = AF_INET6;
|
|
if (pct) {
|
|
/* grab the scope ID */
|
|
slen = len - (alen + 1);
|
|
if (slen <= 0 || slen > 12)
|
|
return 0;
|
|
memcpy(scope_id, pct + 1, slen);
|
|
scope_id[slen] = '\0';
|
|
|
|
rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id);
|
|
rc = (rc == 0) ? 1 : 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
cifs_set_port(struct sockaddr *addr, const unsigned short int port)
|
|
{
|
|
switch (addr->sa_family) {
|
|
case AF_INET:
|
|
((struct sockaddr_in *)addr)->sin_port = htons(port);
|
|
break;
|
|
case AF_INET6:
|
|
((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* The following are taken from fs/ntfs/util.c */
|
|
|
|
#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).
|
|
*/
|
|
struct timespec64
|
|
cifs_NTtimeToUnix(__le64 ntutc)
|
|
{
|
|
struct timespec64 ts;
|
|
/* BB what about the timezone? BB */
|
|
|
|
/* Subtract the NTFS time offset, then convert to 1s intervals. */
|
|
s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
|
|
u64 abs_t;
|
|
|
|
/*
|
|
* Unfortunately can not use normal 64 bit division on 32 bit arch, but
|
|
* the alternative, do_div, does not work with negative numbers so have
|
|
* to special case them
|
|
*/
|
|
if (t < 0) {
|
|
abs_t = -t;
|
|
ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100);
|
|
ts.tv_nsec = -ts.tv_nsec;
|
|
ts.tv_sec = -abs_t;
|
|
} else {
|
|
abs_t = t;
|
|
ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100;
|
|
ts.tv_sec = abs_t;
|
|
}
|
|
|
|
return ts;
|
|
}
|
|
|
|
/* Convert the Unix UTC into NT UTC. */
|
|
u64
|
|
cifs_UnixTimeToNT(struct timespec64 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 const int total_days_of_prev_months[] = {
|
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
|
};
|
|
|
|
struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
|
|
{
|
|
struct timespec64 ts;
|
|
time64_t sec, days;
|
|
int min, day, 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;
|
|
|
|
cifs_dbg(FYI, "date %d time %d\n", date, time);
|
|
|
|
sec = 2 * st->TwoSeconds;
|
|
min = st->Minutes;
|
|
if ((sec > 59) || (min > 59))
|
|
cifs_dbg(VFS, "Invalid time min %d sec %lld\n", min, sec);
|
|
sec += (min * 60);
|
|
sec += 60 * 60 * st->Hours;
|
|
if (st->Hours > 24)
|
|
cifs_dbg(VFS, "Invalid hours %d\n", st->Hours);
|
|
day = sd->Day;
|
|
month = sd->Month;
|
|
if (day < 1 || day > 31 || month < 1 || month > 12) {
|
|
cifs_dbg(VFS, "Invalid date, month %d day: %d\n", month, day);
|
|
day = clamp(day, 1, 31);
|
|
month = clamp(month, 1, 12);
|
|
}
|
|
month -= 1;
|
|
days = day + 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 (divisible 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;
|
|
|
|
/* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */
|
|
|
|
ts.tv_nsec = 0;
|
|
return ts;
|
|
}
|