• stupid_asshole69 [none/use name]@hexbear.net
      link
      fedilink
      English
      arrow-up
      1
      ·
      5 hours ago

      Eh, part of the product is being able to predict drive failure and show users information.

      There are plenty of drives on the secondhand market now that have had their information partially or fully wiped or otherwise modified so in order to preserve that ability to show synology users something approaching a decent prediction they said you can only use approved devices.

      This isn’t the first time a company has done this exact thing. It used to be really common with the big name infrastructure providers. You couldn’t use an ibm/dell/hp/whoever storage doohicky without using their (re)branded drives.

      It used to get lambasted as a cheap attempt to get more cash by upcharging a captive customer, but nowadays I tend to attribute it to unique market conditions around manufacturing, availability and cost versus cost per unit storage.

      The first time i have seen it happen was back when everyone realized that aside from physical mechanical failure where the no no parts get touched, age wasn’t nothing but a number to these drives. Suddenly you’d get people reflashing chips that had reached eol just so their 80 megabyte drive could keep on truckin. Obviously it was fine, but that meant there would be reduced demand for the next generation and overall negative revenue growth so everyone started cracking down on unsupported disks. That’s not the whole story, because the 90s and early 0s are the time when fast hard drives allowed programmers to take advantage of swap space like never before to make up for a lack of memory in the face of ballooning datasets, but it’s a real thing that happened alongside big time storage availability becoming everyday.

      There was another time back during the switch to sata and again as the 6gbps sata standard was allegedly sunset.

    • invalidusernamelol [he/him]@hexbear.net
      link
      fedilink
      English
      arrow-up
      12
      ·
      1 day ago

      Their enclosures are dogshit too. You can roll your own for like half the price if you have spare components laying around or a decent local computer thrift store. It’ll be like 10x better to because you can put actual real NAS software on there, or just run a virtualization server.

      Only benefit for these style appliances is that they usually come with 2.5Gig + network adapters, but a smaller ATX build could just get a PCIE card that does 10 and that benefit is gone.

        • decaptcha [none/use name]@hexbear.net
          link
          fedilink
          English
          arrow-up
          3
          ·
          17 hours ago

          For software… TrueNAS is probably worth checking out but may require some tinkering. I’ve been running it since it was called FreeNAS, about 10 years now actually. It’s been rock solid but I decided to splash out on enterprise grade hardware at that time. Like, actually tracked down and bought the exact RAM modules from the mobo QVL type shit. I guess it was worth, bc I have had almost 0 issues with it once I got it set up the way I liked. I scarcely even log into the admin console anymore, it can be very hands off, set-and-forget, or what have you. I’m only asking it to be a NAS though. I used to fuck with the jail feature so I could have it also run services (bittorrent client, DVR, Plex, etc) but that was a huge pain in the ass for me so I ditched that idea and run services on a different machine now which I very much prefer. The newer TrueNAS versions may make this easier, I dunno.

          If I were starting from scratch today, I’d do it on surplus enterprise hardware (from ebay or a local computer thrift shop) and see if I could make copyparty work for me. Looks like a real nice app that could maybe do everything I need out of a fileserver, but I have no time to fuck with it and my needs are already met.

          • bigpharmasutra [he/him]@hexbear.net
            link
            fedilink
            English
            arrow-up
            1
            ·
            11 hours ago

            This sounds incredibly complicated. Basically I just want to setup a storage setup with redundancy and a silent machine. I’ve got a Synology already but with the hard drives in it its way too annoying to keep in the same room so i hardly use it.

            • BountifulEggnog [she/her]@hexbear.net
              link
              fedilink
              English
              arrow-up
              1
              ·
              6 hours ago

              I don’t own one, but look into n100/n150 machines. I had been looking at These but decided to upgrade an old pc instead. But the cpu is only a few watts so should be nice and quiet. Forget which exact reviews I’ve seen of it

          • invalidusernamelol [he/him]@hexbear.net
            link
            fedilink
            English
            arrow-up
            2
            ·
            14 hours ago

            Copyparty is cool, but it’s still a very green app. If you want to use that, I’d run it as a container within your TrueNAS/FreeNAS install. That way you get the foundation of the tested software with a fun UI added on top.

            Also having read some of the copy party source, it was written by a madman

            • decaptcha [none/use name]@hexbear.net
              link
              fedilink
              English
              arrow-up
              2
              ·
              edit-2
              12 hours ago

              Yeah for sure if I get time to tinker I’ll just run copyparty separately, and that’s probably good advice for everyone, thanks. Wdym a madman? Did they leave a manifesto in the comments?

              • invalidusernamelol [he/him]@hexbear.net
                link
                fedilink
                English
                arrow-up
                1
                ·
                5 hours ago

                There’s goofy design patterns and fun comments all over lol, the whole project is super cool, but absolutely someone’s passion project.

                I say this as someone who deeply respects what they have been able to accomplish. Namely making a Python application that can run on most versions of Python 2.7+ and Python 3.x.

                If you look at a lot of the backend stuff, they use a huge number of single and double character variables that live for the duration of an object and primarily use classes as namespaces. I’m just more used to seeing Python code that builds out clear abstractions and interfaces with the existing data model or implements interaction with that data model so you can use each component separately. Here’s an example of an __init__ for one of their monolithic classes (it’s ~5k lines in total)

                spoiler
                class SvcHub(object):
                    """
                    Hosts all services which cannot be parallelized due to reliance on monolithic resources.
                    Creates a Broker which does most of the heavy stuff; hosted services can use this to perform work:
                        hub.broker.<say|ask>(destination, args_list).
                
                    Either BrokerThr (plain threads) or BrokerMP (multiprocessing) is used depending on configuration.
                    Nothing is returned synchronously; if you want any value returned from the call,
                    put() can return a queue (if want_reply=True) which has a blocking get() with the response.
                    """
                
                    def __init__(
                        self,
                        args: argparse.Namespace,
                        dargs: argparse.Namespace,
                        argv: list[str],
                        printed: str,
                    ) -> None:
                        self.args = args
                        self.dargs = dargs
                        self.argv = argv
                        self.E: EnvParams = args.E
                        self.no_ansi = args.no_ansi
                        self.tz = UTC if args.log_utc else None
                        self.logf: Optional[typing.TextIO] = None
                        self.logf_base_fn = ""
                        self.is_dut = False  # running in unittest; always False
                        self.stop_req = False
                        self.stopping = False
                        self.stopped = False
                        self.reload_req = False
                        self.reload_mutex = threading.Lock()
                        self.stop_cond = threading.Condition()
                        self.nsigs = 3
                        self.retcode = 0
                        self.httpsrv_up = 0
                        self.qr_tsz = None
                
                        self.log_mutex = threading.Lock()
                        self.cday = 0
                        self.cmon = 0
                        self.tstack = 0.0
                
                        self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8)
                
                        if args.sss or args.s >= 3:
                            args.ss = True
                            args.no_dav = True
                            args.no_logues = True
                            args.no_readme = True
                            args.lo = args.lo or "cpp-%Y-%m%d-%H%M%S.txt.xz"
                            args.ls = args.ls or "**,*,ln,p,r"
                
                        if args.ss or args.s >= 2:
                            args.s = True
                            args.unpost = 0
                            args.no_del = True
                            args.no_mv = True
                            args.reflink = True
                            args.dav_auth = True
                            args.vague_403 = True
                            args.nih = True
                
                        if args.s:
                            args.dotpart = True
                            args.no_thumb = True
                            args.no_mtag_ff = True
                            args.no_robots = True
                            args.force_js = True
                
                        if not self._process_config():
                            raise Exception(BAD_CFG)
                
                        # for non-http clients (ftp, tftp)
                        self.bans: dict[str, int] = {}
                        self.gpwd = Garda(self.args.ban_pw)
                        self.gpwc = Garda(self.args.ban_pwc)
                        self.g404 = Garda(self.args.ban_404)
                        self.g403 = Garda(self.args.ban_403)
                        self.g422 = Garda(self.args.ban_422, False)
                        self.gmal = Garda(self.args.ban_422)
                        self.gurl = Garda(self.args.ban_url)
                
                        self.log_div = 10 ** (6 - args.log_tdec)
                        self.log_efmt = "%02d:%02d:%02d.%0{}d".format(args.log_tdec)
                        self.log_dfmt = "%04d-%04d-%06d.%0{}d".format(args.log_tdec)
                        self.log = self._log_disabled if args.q else self._log_enabled
                        if args.lo:
                            self._setup_logfile(printed)
                
                        lg = logging.getLogger()
                        lh = HLog(self.log)
                        lg.handlers = [lh]
                        lg.setLevel(logging.DEBUG)
                
                        self._check_env()
                
                        if args.stackmon:
                            start_stackmon(args.stackmon, 0)
                
                        if args.log_thrs:
                            start_log_thrs(self.log, args.log_thrs, 0)
                
                        if not args.use_fpool and args.j != 1:
                            args.no_fpool = True
                            t = "multithreading enabled with -j {}, so disabling fpool -- this can reduce upload performance on some filesystems, and make some antivirus-softwares "
                            c = 0
                            if ANYWIN:
                                t += "(especially Microsoft Defender) stress your CPU and HDD severely during big uploads"
                                c = 3
                            else:
                                t += "consume more resources (CPU/HDD) than normal"
                            self.log("root", t.format(args.j), c)
                
                        if not args.no_fpool and args.j != 1:
                            t = "WARNING: ignoring --use-fpool because multithreading (-j{}) is enabled"
                            self.log("root", t.format(args.j), c=3)
                            args.no_fpool = True
                
                        for name, arg in (
                            ("iobuf", "iobuf"),
                            ("s-rd-sz", "s_rd_sz"),
                            ("s-wr-sz", "s_wr_sz"),
                        ):
                            zi = getattr(args, arg)
                            if zi < 32768:
                                t = "WARNING: expect very poor performance because you specified a very low value (%d) for --%s"
                                self.log("root", t % (zi, name), 3)
                                zi = 2
                            zi2 = 2 ** (zi - 1).bit_length()
                            if zi != zi2:
                                zi3 = 2 ** ((zi - 1).bit_length() - 1)
                                t = "WARNING: expect poor performance because --%s is not a power-of-two; consider using %d or %d instead of %d"
                                self.log("root", t % (name, zi2, zi3, zi), 3)
                
                        if args.s_rd_sz > args.iobuf:
                            t = "WARNING: --s-rd-sz (%d) is larger than --iobuf (%d); this may lead to reduced performance"
                            self.log("root", t % (args.s_rd_sz, args.iobuf), 3)
                
                        zs = ""
                        if args.th_ram_max < 0.22:
                            zs = "generate thumbnails"
                        elif args.th_ram_max < 1:
                            zs = "generate audio waveforms or spectrograms"
                        if zs:
                            t = "WARNING: --th-ram-max is very small (%.2f GiB); will not be able to %s"
                            self.log("root", t % (args.th_ram_max, zs), 3)
                
                        if args.chpw and args.have_idp_hdrs and "pw" not in args.auth_ord.split(","):
                            t = "ERROR: user-changeable passwords is not compatible with your current configuration. Choose one of these options to fix it:\n option1: disable --chpw\n option2: remove all use of IdP features; --idp-*\n option3: change --auth-ord to something like pw,idp,ipu"
                            self.log("root", t, 1)
                            raise Exception(t)
                
                        noch = set()
                        for zs in args.chpw_no or []:
                            zsl = [x.strip() for x in zs.split(",")]
                            noch.update([x for x in zsl if x])
                        args.chpw_no = noch
                
                        if args.ipu:
                            iu, nm = load_ipu(self.log, args.ipu, True)
                            setattr(args, "ipu_iu", iu)
                            setattr(args, "ipu_nm", nm)
                
                        if args.ipr:
                            ipr = load_ipr(self.log, args.ipr, True)
                            setattr(args, "ipr_u", ipr)
                
                        for zs in "ah_salt fk_salt dk_salt".split():
                            if getattr(args, "show_%s" % (zs,)):
                                self.log("root", "effective %s is %s" % (zs, getattr(args, zs)))
                
                        if args.ah_cli or args.ah_gen:
                            args.idp_store = 0
                            args.no_ses = True
                            args.shr = ""
                
                        if args.idp_store and args.have_idp_hdrs:
                            self.setup_db("idp")
                
                        if not self.args.no_ses:
                            self.setup_db("ses")
                
                        args.shr1 = ""
                        if args.shr:
                            self.setup_share_db()
                
                        bri = "zy"[args.theme % 2 :][:1]
                        ch = "abcdefghijklmnopqrstuvwx"[int(args.theme / 2)]
                        args.theme = "{0}{1} {0} {1}".format(ch, bri)
                
                        if args.nid:
                            args.du_who = "no"
                        args.du_iwho = n_du_who(args.du_who)
                
                        if args.ver and args.ver_who == "no":
                            args.ver_who = "all"
                        args.ver_iwho = n_ver_who(args.ver_who)
                
                        if args.nih:
                            args.vname = ""
                            args.doctitle = args.doctitle.replace(" @ --name", "")
                        else:
                            args.vname = args.name
                        args.doctitle = args.doctitle.replace("--name", args.vname)
                        args.bname = args.bname.replace("--name", args.vname) or args.vname
                
                        if args.log_fk:
                            args.log_fk = re.compile(args.log_fk)
                
                        # initiate all services to manage
                        self.asrv = AuthSrv(self.args, self.log, dargs=self.dargs)
                        ramdisk_chk(self.asrv)
                
                        if args.cgen:
                            self.asrv.cgen()
                
                        if args.exit == "cfg":
                            sys.exit(0)
                
                        if args.ls:
                            self.asrv.dbg_ls()
                
                        if not ANYWIN:
                            self._setlimits()
                
                        self.log("root", "max clients: {}".format(self.args.nc))
                
                        self.tcpsrv = TcpSrv(self)
                
                        if not self.tcpsrv.srv and self.args.ign_ebind_all:
                            self.args.no_fastboot = True
                
                        self.up2k = Up2k(self)
                
                        self._feature_test()
                
                        decs = {k.strip(): 1 for k in self.args.th_dec.split(",")}
                        if not HAVE_VIPS:
                            decs.pop("vips", None)
                        if not HAVE_PIL:
                            decs.pop("pil", None)
                        if not HAVE_RAW:
                            decs.pop("raw", None)
                        if not HAVE_FFMPEG or not HAVE_FFPROBE:
                            decs.pop("ff", None)
                
                        # compressed formats; "s3z=s3m.zip, s3gz=s3m.gz, ..."
                        zlss = [x.strip().lower().split("=", 1) for x in args.au_unpk.split(",")]
                        args.au_unpk = {x[0]: x[1] for x in zlss}
                
                        self.args.th_dec = list(decs.keys())
                        self.thumbsrv = None
                        want_ff = False
                        if not args.no_thumb:
                            t = ", ".join(self.args.th_dec) or "(None available)"
                            self.log("thumb", "decoder preference: {}".format(t))
                ... # I hit the Hexbear character limit