Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *    HMC Drive FTP Services
0004  *
0005  *    Copyright IBM Corp. 2013
0006  *    Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
0007  */
0008 
0009 #define KMSG_COMPONENT "hmcdrv"
0010 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0011 
0012 #include <linux/kernel.h>
0013 #include <linux/slab.h>
0014 #include <linux/uaccess.h>
0015 #include <linux/export.h>
0016 
0017 #include <linux/ctype.h>
0018 #include <linux/crc16.h>
0019 
0020 #include "hmcdrv_ftp.h"
0021 #include "hmcdrv_cache.h"
0022 #include "sclp_ftp.h"
0023 #include "diag_ftp.h"
0024 
0025 /**
0026  * struct hmcdrv_ftp_ops - HMC drive FTP operations
0027  * @startup: startup function
0028  * @shutdown: shutdown function
0029  * @transfer: FTP transfer function
0030  */
0031 struct hmcdrv_ftp_ops {
0032     int (*startup)(void);
0033     void (*shutdown)(void);
0034     ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp,
0035                 size_t *fsize);
0036 };
0037 
0038 static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len);
0039 static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp);
0040 
0041 static const struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */
0042 static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */
0043 static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */
0044 
0045 /**
0046  * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string
0047  * @cmd: FTP command string (NOT zero-terminated)
0048  * @len: length of FTP command string in @cmd
0049  */
0050 static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len)
0051 {
0052     /* HMC FTP command descriptor */
0053     struct hmcdrv_ftp_cmd_desc {
0054         const char *str;       /* command string */
0055         enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */
0056     };
0057 
0058     /* Description of all HMC drive FTP commands
0059      *
0060      * Notes:
0061      * 1. Array size should be a prime number.
0062      * 2. Do not change the order of commands in table (because the
0063      *    index is determined by CRC % ARRAY_SIZE).
0064      * 3. Original command 'nlist' was renamed, else the CRC would
0065      *    collide with 'append' (see point 2).
0066      */
0067     static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = {
0068 
0069         {.str = "get", /* [0] get (CRC = 0x68eb) */
0070          .cmd = HMCDRV_FTP_GET},
0071         {.str = "dir", /* [1] dir (CRC = 0x6a9e) */
0072          .cmd = HMCDRV_FTP_DIR},
0073         {.str = "delete", /* [2] delete (CRC = 0x53ae) */
0074          .cmd = HMCDRV_FTP_DELETE},
0075         {.str = "nls", /* [3] nls (CRC = 0xf87c) */
0076          .cmd = HMCDRV_FTP_NLIST},
0077         {.str = "put", /* [4] put (CRC = 0xac56) */
0078          .cmd = HMCDRV_FTP_PUT},
0079         {.str = "append", /* [5] append (CRC = 0xf56e) */
0080          .cmd = HMCDRV_FTP_APPEND},
0081         {.str = NULL} /* [6] unused */
0082     };
0083 
0084     const struct hmcdrv_ftp_cmd_desc *pdesc;
0085 
0086     u16 crc = 0xffffU;
0087 
0088     if (len == 0)
0089         return HMCDRV_FTP_NOOP; /* error indiactor */
0090 
0091     crc = crc16(crc, cmd, len);
0092     pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds));
0093     pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n",
0094          cmd, crc, (crc % ARRAY_SIZE(ftpcmds)));
0095 
0096     if (!pdesc->str || strncmp(pdesc->str, cmd, len))
0097         return HMCDRV_FTP_NOOP;
0098 
0099     pr_debug("FTP command '%s' found, with ID %d\n",
0100          pdesc->str, pdesc->cmd);
0101 
0102     return pdesc->cmd;
0103 }
0104 
0105 /**
0106  * hmcdrv_ftp_parse() - HMC drive FTP command parser
0107  * @cmd: FTP command string "<cmd> <filename>"
0108  * @ftp: Pointer to FTP command specification buffer (output)
0109  *
0110  * Return: 0 on success, else a (negative) error code
0111  */
0112 static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp)
0113 {
0114     char *start;
0115     int argc = 0;
0116 
0117     ftp->id = HMCDRV_FTP_NOOP;
0118     ftp->fname = NULL;
0119 
0120     while (*cmd != '\0') {
0121 
0122         while (isspace(*cmd))
0123             ++cmd;
0124 
0125         if (*cmd == '\0')
0126             break;
0127 
0128         start = cmd;
0129 
0130         switch (argc) {
0131         case 0: /* 1st argument (FTP command) */
0132             while ((*cmd != '\0') && !isspace(*cmd))
0133                 ++cmd;
0134             ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start);
0135             break;
0136         case 1: /* 2nd / last argument (rest of line) */
0137             while ((*cmd != '\0') && !iscntrl(*cmd))
0138                 ++cmd;
0139             ftp->fname = start;
0140             fallthrough;
0141         default:
0142             *cmd = '\0';
0143             break;
0144         } /* switch */
0145 
0146         ++argc;
0147     } /* while */
0148 
0149     if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP))
0150         return -EINVAL;
0151 
0152     return 0;
0153 }
0154 
0155 /**
0156  * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space
0157  * @ftp: pointer to FTP command specification
0158  *
0159  * Return: number of bytes read/written or a negative error code
0160  */
0161 ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp)
0162 {
0163     ssize_t len;
0164 
0165     mutex_lock(&hmcdrv_ftp_mutex);
0166 
0167     if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) {
0168         pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n",
0169              ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
0170         len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer);
0171     } else {
0172         len = -ENXIO;
0173     }
0174 
0175     mutex_unlock(&hmcdrv_ftp_mutex);
0176     return len;
0177 }
0178 EXPORT_SYMBOL(hmcdrv_ftp_do);
0179 
0180 /**
0181  * hmcdrv_ftp_probe() - probe for the HMC drive FTP service
0182  *
0183  * Return: 0 if service is available, else an (negative) error code
0184  */
0185 int hmcdrv_ftp_probe(void)
0186 {
0187     int rc;
0188 
0189     struct hmcdrv_ftp_cmdspec ftp = {
0190         .id = HMCDRV_FTP_NOOP,
0191         .ofs = 0,
0192         .fname = "",
0193         .len = PAGE_SIZE
0194     };
0195 
0196     ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
0197 
0198     if (!ftp.buf)
0199         return -ENOMEM;
0200 
0201     rc = hmcdrv_ftp_startup();
0202 
0203     if (rc)
0204         goto out;
0205 
0206     rc = hmcdrv_ftp_do(&ftp);
0207     hmcdrv_ftp_shutdown();
0208 
0209     switch (rc) {
0210     case -ENOENT: /* no such file/media or currently busy, */
0211     case -EBUSY:  /* but service seems to be available */
0212         rc = 0;
0213         break;
0214     default: /* leave 'rc' as it is for [0, -EPERM, -E...] */
0215         if (rc > 0)
0216             rc = 0; /* clear length (success) */
0217         break;
0218     } /* switch */
0219 out:
0220     free_page((unsigned long) ftp.buf);
0221     return rc;
0222 }
0223 EXPORT_SYMBOL(hmcdrv_ftp_probe);
0224 
0225 /**
0226  * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space
0227  *
0228  * @cmd: FTP command string "<cmd> <filename>"
0229  * @offset: file position to read/write
0230  * @buf: user-space buffer for read/written directory/file
0231  * @len: size of @buf (read/dir) or number of bytes to write
0232  *
0233  * This function must not be called before hmcdrv_ftp_startup() was called.
0234  *
0235  * Return: number of bytes read/written or a negative error code
0236  */
0237 ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset,
0238                char __user *buf, size_t len)
0239 {
0240     int order;
0241 
0242     struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset};
0243     ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp);
0244 
0245     if (retlen)
0246         return retlen;
0247 
0248     order = get_order(ftp.len);
0249     ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order);
0250 
0251     if (!ftp.buf)
0252         return -ENOMEM;
0253 
0254     switch (ftp.id) {
0255     case HMCDRV_FTP_DIR:
0256     case HMCDRV_FTP_NLIST:
0257     case HMCDRV_FTP_GET:
0258         retlen = hmcdrv_ftp_do(&ftp);
0259 
0260         if ((retlen >= 0) &&
0261             copy_to_user(buf, ftp.buf, retlen))
0262             retlen = -EFAULT;
0263         break;
0264 
0265     case HMCDRV_FTP_PUT:
0266     case HMCDRV_FTP_APPEND:
0267         if (!copy_from_user(ftp.buf, buf, ftp.len))
0268             retlen = hmcdrv_ftp_do(&ftp);
0269         else
0270             retlen = -EFAULT;
0271         break;
0272 
0273     case HMCDRV_FTP_DELETE:
0274         retlen = hmcdrv_ftp_do(&ftp);
0275         break;
0276 
0277     default:
0278         retlen = -EOPNOTSUPP;
0279         break;
0280     }
0281 
0282     free_pages((unsigned long) ftp.buf, order);
0283     return retlen;
0284 }
0285 
0286 /**
0287  * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a
0288  * dedicated (owner) instance
0289  *
0290  * Return: 0 on success, else an (negative) error code
0291  */
0292 int hmcdrv_ftp_startup(void)
0293 {
0294     static const struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = {
0295         .startup = diag_ftp_startup,
0296         .shutdown = diag_ftp_shutdown,
0297         .transfer = diag_ftp_cmd
0298     };
0299 
0300     static const struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = {
0301         .startup = sclp_ftp_startup,
0302         .shutdown = sclp_ftp_shutdown,
0303         .transfer = sclp_ftp_cmd
0304     };
0305 
0306     int rc = 0;
0307 
0308     mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */
0309 
0310     if (hmcdrv_ftp_refcnt == 0) {
0311         if (MACHINE_IS_VM)
0312             hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm;
0313         else if (MACHINE_IS_LPAR || MACHINE_IS_KVM)
0314             hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar;
0315         else
0316             rc = -EOPNOTSUPP;
0317 
0318         if (hmcdrv_ftp_funcs)
0319             rc = hmcdrv_ftp_funcs->startup();
0320     }
0321 
0322     if (!rc)
0323         ++hmcdrv_ftp_refcnt;
0324 
0325     mutex_unlock(&hmcdrv_ftp_mutex);
0326     return rc;
0327 }
0328 EXPORT_SYMBOL(hmcdrv_ftp_startup);
0329 
0330 /**
0331  * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a
0332  * dedicated (owner) instance
0333  */
0334 void hmcdrv_ftp_shutdown(void)
0335 {
0336     mutex_lock(&hmcdrv_ftp_mutex);
0337     --hmcdrv_ftp_refcnt;
0338 
0339     if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs)
0340         hmcdrv_ftp_funcs->shutdown();
0341 
0342     mutex_unlock(&hmcdrv_ftp_mutex);
0343 }
0344 EXPORT_SYMBOL(hmcdrv_ftp_shutdown);